mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 09:11:52 -04:00
452b478bb3
* Fix already downloaded toolchain re-use the following mv command would move $work into $toolchain_dir if the $toolchain_dir folder already existed. * resolve z-fighting
608 lines
20 KiB
C++
608 lines
20 KiB
C++
#include "tex_copy_conv.hpp"
|
|
#include "tex_copy_format_contract.hpp"
|
|
|
|
#include "../internal.hpp"
|
|
#include "../gx/gx.hpp"
|
|
#include "../webgpu/gpu.hpp"
|
|
#include "texture.hpp"
|
|
#include "../gx/gx_fmt.hpp"
|
|
|
|
#include <absl/container/flat_hash_map.h>
|
|
|
|
#include "texture_convert.hpp"
|
|
|
|
using namespace std::string_literals;
|
|
|
|
namespace aurora::gfx::tex_copy_conv {
|
|
static Module Log("aurora::gfx::tex_copy_conv");
|
|
|
|
using webgpu::g_device;
|
|
|
|
static constexpr std::string_view ShaderPreamble = R"(
|
|
@group(0) @binding(0) var src_samp: sampler;
|
|
@group(0) @binding(1) var src: texture_2d<f32>;
|
|
|
|
struct UVTransform {
|
|
offset: vec2f,
|
|
scale: vec2f,
|
|
copy_filter: vec4f,
|
|
flags: vec4f,
|
|
};
|
|
@group(0) @binding(2) var<uniform> uv_xf: UVTransform;
|
|
|
|
struct VertexOutput {
|
|
@builtin(position) pos: vec4f,
|
|
@location(0) uv: vec2f,
|
|
};
|
|
|
|
var<private> positions: array<vec2f, 3> = array(
|
|
vec2f(-1.0, 1.0),
|
|
vec2f(-1.0, -3.0),
|
|
vec2f(3.0, 1.0),
|
|
);
|
|
var<private> uvs: array<vec2f, 3> = array(
|
|
vec2f(0.0, 0.0),
|
|
vec2f(0.0, 2.0),
|
|
vec2f(2.0, 0.0),
|
|
);
|
|
|
|
@vertex fn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput {
|
|
var out: VertexOutput;
|
|
out.pos = vec4f(positions[vi], 0.0, 1.0);
|
|
out.uv = uvs[vi] * uv_xf.scale + uv_xf.offset;
|
|
return out;
|
|
}
|
|
|
|
fn intensity(rgb: vec3f) -> f32 {
|
|
// ITU-R BT.601 luma coefficients
|
|
return dot(rgb, vec3f(0.257, 0.504, 0.098)) + 16.0 / 255.0;
|
|
}
|
|
|
|
fn quantize4(v: f32) -> f32 {
|
|
return floor(v * 16.0) / 15.0;
|
|
}
|
|
|
|
fn apply_opaque_alpha(c: vec4f) -> vec4f {
|
|
if (uv_xf.flags.x != 0.0) {
|
|
return vec4f(c.rgb, 1.0);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
fn clamp_copy_uv(uv: vec2f) -> vec2f {
|
|
return vec2f(uv.x, clamp(uv.y, uv_xf.flags.z, uv_xf.flags.w));
|
|
}
|
|
|
|
fn sample_copy(uv: vec2f) -> vec4f {
|
|
let current = textureSample(src, src_samp, clamp_copy_uv(uv));
|
|
if (uv_xf.copy_filter.w == 0.0) {
|
|
return apply_opaque_alpha(current);
|
|
}
|
|
|
|
let tex_size = vec2f(textureDimensions(src));
|
|
let pixel_size = vec2f(1.0, 1.0) / tex_size;
|
|
let row_stride = max(uv_xf.flags.y, 1.0);
|
|
let prev = textureSample(src, src_samp,
|
|
clamp_copy_uv(uv - vec2f(0.0, pixel_size.y * row_stride)));
|
|
let next = textureSample(src, src_samp,
|
|
clamp_copy_uv(uv + vec2f(0.0, pixel_size.y * row_stride)));
|
|
let prev_rgb = floor(prev.rgb * 255.0 + vec3f(0.5));
|
|
let current_rgb = floor(current.rgb * 255.0 + vec3f(0.5));
|
|
let next_rgb = floor(next.rgb * 255.0 + vec3f(0.5));
|
|
let filtered_rgb = min(
|
|
floor((prev_rgb * uv_xf.copy_filter.x +
|
|
current_rgb * uv_xf.copy_filter.y +
|
|
next_rgb * uv_xf.copy_filter.z) / 64.0),
|
|
vec3f(255.0));
|
|
let filtered = vec4f(filtered_rgb / 255.0, current.a);
|
|
return apply_opaque_alpha(filtered);
|
|
}
|
|
)"sv;
|
|
|
|
static const std::string DepthShaderPreamble = R"(
|
|
@group(0) @binding(0) var src: texture_depth_2d;
|
|
|
|
struct UVTransform {
|
|
offset: vec2f,
|
|
scale: vec2f,
|
|
copy_filter: vec4f,
|
|
flags: vec4f,
|
|
};
|
|
@group(0) @binding(1) var<uniform> uv_xf: UVTransform;
|
|
|
|
struct VertexOutput {
|
|
@builtin(position) pos: vec4f,
|
|
@location(0) uv: vec2f,
|
|
};
|
|
|
|
var<private> positions: array<vec2f, 3> = array(
|
|
vec2f(-1.0, 1.0),
|
|
vec2f(-1.0, -3.0),
|
|
vec2f(3.0, 1.0),
|
|
);
|
|
var<private> uvs: array<vec2f, 3> = array(
|
|
vec2f(0.0, 0.0),
|
|
vec2f(0.0, 2.0),
|
|
vec2f(2.0, 0.0),
|
|
);
|
|
|
|
@vertex fn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput {
|
|
var out: VertexOutput;
|
|
out.pos = vec4f(positions[vi], 0.0, 1.0);
|
|
out.uv = uvs[vi] * uv_xf.scale + uv_xf.offset;
|
|
return out;
|
|
}
|
|
)"s + (gx::UseReversedZ ? R"(
|
|
fn gx_z24_at_coord(unclamped_coord: vec2i) -> u32 {
|
|
let tex_size = vec2i(textureDimensions(src));
|
|
let coord = clamp(unclamped_coord, vec2i(0), tex_size - vec2i(1));
|
|
let depth = textureLoad(src, coord, 0);
|
|
return min(u32(clamp(1.0 - depth, 0.0, 1.0) * 16777215.0 + 0.5), 0x00ffffffu);
|
|
}
|
|
)"s
|
|
: R"(
|
|
fn gx_z24_at_coord(unclamped_coord: vec2i) -> u32 {
|
|
let tex_size = vec2i(textureDimensions(src));
|
|
let coord = clamp(unclamped_coord, vec2i(0), tex_size - vec2i(1));
|
|
let depth = textureLoad(src, coord, 0);
|
|
return min(u32(clamp(depth, 0.0, 1.0) * 16777215.0 + 0.5), 0x00ffffffu);
|
|
}
|
|
)"s) + R"(
|
|
fn gx_depth_bytes(z24: u32) -> vec3u {
|
|
return vec3u((z24 >> 16u) & 0xffu, (z24 >> 8u) & 0xffu, z24 & 0xffu);
|
|
}
|
|
|
|
fn clamp_copy_coord(coord: vec2i, tex_size: vec2i) -> vec2i {
|
|
let top = i32(floor(uv_xf.flags.z * f32(tex_size.y)));
|
|
let bottom = i32(floor(uv_xf.flags.w * f32(tex_size.y)));
|
|
return vec2i(coord.x, clamp(coord.y, top, bottom));
|
|
}
|
|
|
|
fn sample_depth_copy(uv: vec2f) -> vec4u {
|
|
let tex_size = vec2i(textureDimensions(src));
|
|
let current_coord = clamp_copy_coord(vec2i(floor(uv * vec2f(tex_size))), tex_size);
|
|
let current = gx_depth_bytes(gx_z24_at_coord(current_coord));
|
|
if (uv_xf.copy_filter.w == 0.0) {
|
|
return vec4u(current, 255u);
|
|
}
|
|
|
|
// GX applies its vertical copy filter to depth copies too, filtering the high/middle/low Z bytes
|
|
// independently before the destination format picks bytes. Alpha is unfiltered and 255.
|
|
let row_stride = max(i32(round(uv_xf.flags.y)), 1);
|
|
let prev = gx_depth_bytes(gx_z24_at_coord(
|
|
clamp_copy_coord(current_coord - vec2i(0, row_stride), tex_size)));
|
|
let next = gx_depth_bytes(gx_z24_at_coord(
|
|
clamp_copy_coord(current_coord + vec2i(0, row_stride), tex_size)));
|
|
let coefficients = vec3u(uv_xf.copy_filter.xyz);
|
|
let combined = prev * coefficients.x + current * coefficients.y + next * coefficients.z;
|
|
var filtered = combined >> vec3u(6u);
|
|
|
|
// The copy-filter accumulator wraps to nine bits when coefficients can
|
|
// produce values at or above 512, before saturating to an eight-bit byte.
|
|
if (coefficients.x + coefficients.y + coefficients.z >= 128u) {
|
|
filtered = filtered & vec3u(0x1ffu);
|
|
}
|
|
filtered = min(filtered, vec3u(255u));
|
|
return vec4u(filtered, 255u);
|
|
}
|
|
)"s;
|
|
|
|
// Passthrough blit (for scaling)
|
|
static constexpr std::string_view FragPassthrough = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
return sample_copy(in.uv);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_I4: 4-bit intensity -> R8Unorm (quantized)
|
|
static constexpr std::string_view FragI4 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let rgb = sample_copy(in.uv).rgb;
|
|
let i = quantize4(intensity(rgb));
|
|
return vec4f(i, i, i, i);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_I8: 8-bit intensity -> R8Unorm
|
|
static constexpr std::string_view FragI8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let rgb = sample_copy(in.uv).rgb;
|
|
let i = intensity(rgb);
|
|
return vec4f(i, i, i, i);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_IA4: 4-bit intensity + 4-bit alpha -> RG8Unorm
|
|
static constexpr std::string_view FragIA4 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
let i = quantize4(intensity(c.rgb));
|
|
let a = quantize4(c.a);
|
|
return vec4f(i, i, i, a);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_IA8: 8-bit intensity + 8-bit alpha -> RG8Unorm
|
|
static constexpr std::string_view FragIA8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
let i = intensity(c.rgb);
|
|
return vec4f(i, i, i, c.a);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_RGB565: Blit alpha to 1.0
|
|
static constexpr std::string_view FragRGB565 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
return vec4f(c.rgb, 1.0);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_R4: 4-bit red -> R8Unorm
|
|
static constexpr std::string_view FragR4 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let r = quantize4(sample_copy(in.uv).r);
|
|
return vec4f(r, r, r, r);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_RA4: 4-bit red + 4-bit alpha -> RG8Unorm
|
|
static constexpr std::string_view FragRA4 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
let r = quantize4(c.r);
|
|
return vec4f(r, r, r, quantize4(c.a));
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_RA8: 8-bit red + 8-bit alpha -> RG8Unorm
|
|
static constexpr std::string_view FragRA8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
return vec4f(c.r, c.r, c.r, c.a);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_A8: 8-bit alpha -> R8Unorm
|
|
static constexpr std::string_view FragA8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let a = sample_copy(in.uv).a;
|
|
return vec4f(a, a, a, a);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_R8: 8-bit red -> R8Unorm
|
|
static constexpr std::string_view FragR8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let r = sample_copy(in.uv).r;
|
|
return vec4f(r, r, r, r);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_G8: 8-bit green -> R8Unorm
|
|
static constexpr std::string_view FragG8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let g = sample_copy(in.uv).g;
|
|
return vec4f(g, g, g, g);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_B8: 8-bit blue -> R8Unorm
|
|
static constexpr std::string_view FragB8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let b = sample_copy(in.uv).b;
|
|
return vec4f(b, b, b, b);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_RG8: 8-bit red + 8-bit green -> RG8Unorm
|
|
static constexpr std::string_view FragRG8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
return vec4f(c.r, c.r, c.r, c.g);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_CTF_GB8: 8-bit green + 8-bit blue -> RG8Unorm
|
|
static constexpr std::string_view FragGB8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let c = sample_copy(in.uv);
|
|
return vec4f(c.g, c.g, c.g, c.b);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_Z8 stores the high-Z byte in I8 storage, which samples as intensity
|
|
// replicated across all four channels.
|
|
static constexpr std::string_view FragZ8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let depth_bytes = sample_depth_copy(in.uv);
|
|
let z8 = f32(depth_bytes.r) / 255.0;
|
|
return vec4f(z8, z8, z8, z8);
|
|
}
|
|
)"sv;
|
|
|
|
// GX_TF_Z16 is the depth RA8 copy path: depth samples have opaque alpha and their high-Z byte in
|
|
// red, so the encoded [A, R] pair samples through IA8 as high-Z intensity with opaque alpha.
|
|
static constexpr std::string_view FragZ16 = detail::Z16FragmentShader;
|
|
|
|
// GX_TF_Z24X8 uses the RGBA8 storage shape for copies, with the 24-bit depth
|
|
// value available to z-texture sampling through RGB.
|
|
static constexpr std::string_view FragZ24X8 = R"(
|
|
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|
let depth_bytes = sample_depth_copy(in.uv);
|
|
let r = f32(depth_bytes.r) / 255.0;
|
|
let g = f32(depth_bytes.g) / 255.0;
|
|
let b = f32(depth_bytes.b) / 255.0;
|
|
return vec4f(r, g, b, 1.0);
|
|
}
|
|
)"sv;
|
|
|
|
struct ConvPipeline {
|
|
GXTexFmt fmt;
|
|
std::string_view fragShader;
|
|
wgpu::TextureFormat outputFormat;
|
|
const char* label;
|
|
};
|
|
|
|
static constexpr std::array ConvPipelines{
|
|
ConvPipeline{GX_TF_I4, FragI4, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv I4"},
|
|
ConvPipeline{GX_TF_I8, FragI8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv I8"},
|
|
ConvPipeline{GX_TF_IA4, FragIA4, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv IA4"},
|
|
ConvPipeline{GX_TF_IA8, FragIA8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv IA8"},
|
|
ConvPipeline{GX_TF_RGB565, FragRGB565, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv RGB565"},
|
|
ConvPipeline{GX_CTF_R4, FragR4, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv R4"},
|
|
ConvPipeline{GX_CTF_RA4, FragRA4, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv RA4"},
|
|
ConvPipeline{GX_CTF_RA8, FragRA8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv RA8"},
|
|
ConvPipeline{GX_CTF_A8, FragA8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv A8"},
|
|
ConvPipeline{GX_CTF_R8, FragR8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv R8"},
|
|
ConvPipeline{GX_CTF_G8, FragG8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv G8"},
|
|
ConvPipeline{GX_CTF_B8, FragB8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv B8"},
|
|
ConvPipeline{GX_CTF_RG8, FragRG8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv RG8"},
|
|
ConvPipeline{GX_CTF_GB8, FragGB8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv GB8"},
|
|
};
|
|
|
|
static constexpr std::array DepthConvPipelines{
|
|
ConvPipeline{GX_TF_Z8, FragZ8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv Z8"},
|
|
ConvPipeline{GX_TF_Z16, FragZ16, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv Z16"},
|
|
ConvPipeline{GX_TF_Z24X8, FragZ24X8, wgpu::TextureFormat::RGBA8Unorm, "TexCopyConv Z24X8"},
|
|
};
|
|
|
|
static wgpu::BindGroupLayout g_bindGroupLayout;
|
|
static wgpu::BindGroupLayout g_depthBindGroupLayout;
|
|
static wgpu::Sampler g_nearestSampler;
|
|
static wgpu::Sampler g_linearSampler;
|
|
static absl::flat_hash_map<GXTexFmt, wgpu::RenderPipeline> g_pipelines;
|
|
static wgpu::RenderPipeline g_blitPipeline;
|
|
|
|
static wgpu::RenderPipeline create_pipeline(const ConvPipeline& conv, const std::string_view shaderPreamble,
|
|
const wgpu::BindGroupLayout& bindGroupLayout) {
|
|
std::string shaderSource;
|
|
shaderSource.reserve(shaderPreamble.size() + conv.fragShader.size());
|
|
shaderSource += shaderPreamble;
|
|
shaderSource += conv.fragShader;
|
|
|
|
const wgpu::ShaderSourceWGSL wgslSource{wgpu::ShaderSourceWGSL::Init{
|
|
.code = shaderSource.c_str(),
|
|
}};
|
|
const wgpu::ShaderModuleDescriptor moduleDescriptor{
|
|
.nextInChain = &wgslSource,
|
|
.label = conv.label,
|
|
};
|
|
const auto module = g_device.CreateShaderModule(&moduleDescriptor);
|
|
|
|
const std::array colorTargets{wgpu::ColorTargetState{
|
|
.format = conv.outputFormat,
|
|
}};
|
|
const wgpu::FragmentState fragmentState{
|
|
.module = module,
|
|
.entryPoint = "fs_main",
|
|
.targetCount = colorTargets.size(),
|
|
.targets = colorTargets.data(),
|
|
};
|
|
|
|
const wgpu::PipelineLayoutDescriptor layoutDescriptor{
|
|
.bindGroupLayoutCount = 1,
|
|
.bindGroupLayouts = &bindGroupLayout,
|
|
};
|
|
const auto pipelineLayout = g_device.CreatePipelineLayout(&layoutDescriptor);
|
|
|
|
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
|
|
.label = conv.label,
|
|
.layout = pipelineLayout,
|
|
.vertex =
|
|
wgpu::VertexState{
|
|
.module = module,
|
|
.entryPoint = "vs_main",
|
|
},
|
|
.primitive =
|
|
wgpu::PrimitiveState{
|
|
.topology = wgpu::PrimitiveTopology::TriangleList,
|
|
},
|
|
.fragment = &fragmentState,
|
|
};
|
|
return g_device.CreateRenderPipeline(&pipelineDescriptor);
|
|
}
|
|
|
|
bool needs_conversion(const GXTexFmt fmt) { return g_pipelines.contains(fmt); }
|
|
|
|
void initialize() {
|
|
static constexpr std::array bindGroupLayoutEntries{
|
|
wgpu::BindGroupLayoutEntry{
|
|
.binding = 0,
|
|
.visibility = wgpu::ShaderStage::Fragment,
|
|
.sampler =
|
|
wgpu::SamplerBindingLayout{
|
|
.type = wgpu::SamplerBindingType::Filtering,
|
|
},
|
|
},
|
|
wgpu::BindGroupLayoutEntry{
|
|
.binding = 1,
|
|
.visibility = wgpu::ShaderStage::Fragment,
|
|
.texture =
|
|
wgpu::TextureBindingLayout{
|
|
.sampleType = wgpu::TextureSampleType::Float,
|
|
.viewDimension = wgpu::TextureViewDimension::e2D,
|
|
},
|
|
},
|
|
wgpu::BindGroupLayoutEntry{
|
|
.binding = 2,
|
|
.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
|
|
.buffer =
|
|
wgpu::BufferBindingLayout{
|
|
.type = wgpu::BufferBindingType::Uniform,
|
|
},
|
|
},
|
|
};
|
|
static constexpr wgpu::BindGroupLayoutDescriptor bindGroupLayoutDescriptor{
|
|
.label = "TexCopyConv Bind Group Layout",
|
|
.entryCount = bindGroupLayoutEntries.size(),
|
|
.entries = bindGroupLayoutEntries.data(),
|
|
};
|
|
g_bindGroupLayout = g_device.CreateBindGroupLayout(&bindGroupLayoutDescriptor);
|
|
|
|
static constexpr std::array depthBindGroupLayoutEntries{
|
|
wgpu::BindGroupLayoutEntry{
|
|
.binding = 0,
|
|
.visibility = wgpu::ShaderStage::Fragment,
|
|
.texture =
|
|
wgpu::TextureBindingLayout{
|
|
.sampleType = wgpu::TextureSampleType::Depth,
|
|
.viewDimension = wgpu::TextureViewDimension::e2D,
|
|
},
|
|
},
|
|
wgpu::BindGroupLayoutEntry{
|
|
.binding = 1,
|
|
.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
|
|
.buffer =
|
|
wgpu::BufferBindingLayout{
|
|
.type = wgpu::BufferBindingType::Uniform,
|
|
},
|
|
},
|
|
};
|
|
static constexpr wgpu::BindGroupLayoutDescriptor depthBindGroupLayoutDescriptor{
|
|
.label = "TexCopyConv Depth Bind Group Layout",
|
|
.entryCount = depthBindGroupLayoutEntries.size(),
|
|
.entries = depthBindGroupLayoutEntries.data(),
|
|
};
|
|
g_depthBindGroupLayout = g_device.CreateBindGroupLayout(&depthBindGroupLayoutDescriptor);
|
|
|
|
g_blitPipeline = create_pipeline(
|
|
{GX_TF_RGBA8, FragPassthrough, webgpu::g_graphicsConfig.surfaceConfiguration.format, "TexCopyConv Blit"},
|
|
ShaderPreamble, g_bindGroupLayout);
|
|
for (const auto& conv : ConvPipelines) {
|
|
g_pipelines[conv.fmt] = create_pipeline(conv, ShaderPreamble, g_bindGroupLayout);
|
|
if (conv.outputFormat != to_wgpu(conv.fmt)) {
|
|
Log.fatal("Output format mismatch for {}", conv.fmt);
|
|
}
|
|
}
|
|
for (const auto& conv : DepthConvPipelines) {
|
|
g_pipelines[conv.fmt] = create_pipeline(conv, DepthShaderPreamble, g_depthBindGroupLayout);
|
|
if (conv.outputFormat != to_wgpu(conv.fmt)) {
|
|
Log.fatal("Output format mismatch for {}", conv.fmt);
|
|
}
|
|
}
|
|
|
|
static constexpr wgpu::SamplerDescriptor nearestSamplerDescriptor{
|
|
.label = "TexCopyConv Nearest Sampler",
|
|
.magFilter = wgpu::FilterMode::Nearest,
|
|
.minFilter = wgpu::FilterMode::Nearest,
|
|
};
|
|
g_nearestSampler = g_device.CreateSampler(&nearestSamplerDescriptor);
|
|
|
|
static constexpr wgpu::SamplerDescriptor linearSamplerDescriptor{
|
|
.label = "TexCopyConv Linear Sampler",
|
|
.magFilter = wgpu::FilterMode::Linear,
|
|
.minFilter = wgpu::FilterMode::Linear,
|
|
};
|
|
g_linearSampler = g_device.CreateSampler(&linearSamplerDescriptor);
|
|
}
|
|
|
|
void shutdown() {
|
|
g_pipelines.clear();
|
|
g_blitPipeline = {};
|
|
g_bindGroupLayout = {};
|
|
g_depthBindGroupLayout = {};
|
|
g_nearestSampler = {};
|
|
g_linearSampler = {};
|
|
}
|
|
|
|
static void execute(const wgpu::CommandEncoder& cmd, const ConvRequest& req, const wgpu::RenderPipeline& pipeline) {
|
|
wgpu::BindGroup bindGroup;
|
|
if (gx::is_depth_format(req.fmt)) {
|
|
const std::array bindGroupEntries{
|
|
wgpu::BindGroupEntry{
|
|
.binding = 0,
|
|
.textureView = req.srcView,
|
|
},
|
|
wgpu::BindGroupEntry{
|
|
.binding = 1,
|
|
.buffer = g_uniformBuffer,
|
|
.offset = req.uniformRange.offset,
|
|
.size = req.uniformRange.size,
|
|
},
|
|
};
|
|
const wgpu::BindGroupDescriptor bindGroupDescriptor{
|
|
.layout = g_depthBindGroupLayout,
|
|
.entryCount = bindGroupEntries.size(),
|
|
.entries = bindGroupEntries.data(),
|
|
};
|
|
bindGroup = g_device.CreateBindGroup(&bindGroupDescriptor);
|
|
} else {
|
|
const auto& sampler = req.sampleFilter == SampleFilter::Linear ? g_linearSampler : g_nearestSampler;
|
|
const std::array bindGroupEntries{
|
|
wgpu::BindGroupEntry{
|
|
.binding = 0,
|
|
.sampler = sampler,
|
|
},
|
|
wgpu::BindGroupEntry{
|
|
.binding = 1,
|
|
.textureView = req.srcView,
|
|
},
|
|
wgpu::BindGroupEntry{
|
|
.binding = 2,
|
|
.buffer = g_uniformBuffer,
|
|
.offset = req.uniformRange.offset,
|
|
.size = req.uniformRange.size,
|
|
},
|
|
};
|
|
const wgpu::BindGroupDescriptor bindGroupDescriptor{
|
|
.layout = g_bindGroupLayout,
|
|
.entryCount = bindGroupEntries.size(),
|
|
.entries = bindGroupEntries.data(),
|
|
};
|
|
bindGroup = g_device.CreateBindGroup(&bindGroupDescriptor);
|
|
}
|
|
|
|
const std::array colorAttachments{
|
|
wgpu::RenderPassColorAttachment{
|
|
.view = req.dst->attachmentTextureView,
|
|
.loadOp = wgpu::LoadOp::Clear,
|
|
.storeOp = wgpu::StoreOp::Store,
|
|
.clearValue = {0.0, 0.0, 0.0, 0.0},
|
|
},
|
|
};
|
|
const wgpu::RenderPassDescriptor renderPassDescriptor{
|
|
.label = "TexCopyConv Pass",
|
|
.colorAttachmentCount = colorAttachments.size(),
|
|
.colorAttachments = colorAttachments.data(),
|
|
};
|
|
const auto pass = cmd.BeginRenderPass(&renderPassDescriptor);
|
|
pass.SetPipeline(pipeline);
|
|
pass.SetBindGroup(0, bindGroup);
|
|
pass.Draw(3);
|
|
pass.End();
|
|
}
|
|
|
|
void run(const wgpu::CommandEncoder& cmd, const ConvRequest& req) {
|
|
const auto it = g_pipelines.find(req.fmt);
|
|
if (it == g_pipelines.end()) {
|
|
Log.fatal("No copy conversion pipeline for format {}", static_cast<int>(req.fmt));
|
|
}
|
|
execute(cmd, req, it->second);
|
|
}
|
|
|
|
void blit(const wgpu::CommandEncoder& cmd, const ConvRequest& req) { execute(cmd, req, g_blitPipeline); }
|
|
|
|
} // namespace aurora::gfx::tex_copy_conv
|