mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
[D3D12] Fix mips generation issues
Adds handling for typeless/signed formats and texture arrays, fixes crashes in FH and TDU
This commit is contained in:
@@ -86,37 +86,24 @@ namespace shaders {
|
||||
static bool FormatSupportsMipGenerationUAV(DXGI_FORMAT format) {
|
||||
switch (format) {
|
||||
// RGBA formats - most common, widely supported for typed UAV.
|
||||
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_R8G8B8A8_SNORM:
|
||||
case DXGI_FORMAT_R10G10B10A2_TYPELESS:
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM:
|
||||
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
|
||||
case DXGI_FORMAT_R16G16B16A16_UNORM:
|
||||
case DXGI_FORMAT_R16G16B16A16_SNORM:
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||
case DXGI_FORMAT_R32G32B32A32_FLOAT:
|
||||
// RG formats.
|
||||
case DXGI_FORMAT_R8G8_TYPELESS:
|
||||
case DXGI_FORMAT_R8G8_UNORM:
|
||||
case DXGI_FORMAT_R8G8_SNORM:
|
||||
case DXGI_FORMAT_R16G16_TYPELESS:
|
||||
case DXGI_FORMAT_R16G16_UNORM:
|
||||
case DXGI_FORMAT_R16G16_SNORM:
|
||||
case DXGI_FORMAT_R16G16_FLOAT:
|
||||
case DXGI_FORMAT_R32G32_FLOAT:
|
||||
// Single channel formats.
|
||||
case DXGI_FORMAT_R8_TYPELESS:
|
||||
case DXGI_FORMAT_R8_UNORM:
|
||||
case DXGI_FORMAT_R8_SNORM:
|
||||
case DXGI_FORMAT_R16_TYPELESS:
|
||||
case DXGI_FORMAT_R16_UNORM:
|
||||
case DXGI_FORMAT_R16_SNORM:
|
||||
case DXGI_FORMAT_R16_FLOAT:
|
||||
case DXGI_FORMAT_R32_FLOAT:
|
||||
return true;
|
||||
// All other formats (block-compressed, packed, integer, etc.) are not
|
||||
// supported for our float4 typed UAV store mip generation.
|
||||
// All other formats (block-compressed, packed, integer, SNORM, etc.) are
|
||||
// not supported for our float4 typed UAV store mip generation.
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1931,22 +1918,29 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
|
||||
// Generate mip levels for scaled resolve textures via compute shader.
|
||||
if (level_last_for_mip_gen > 0) {
|
||||
uint32_t texture_level_count = texture_key.mip_max_level + 1;
|
||||
if (is_3d) {
|
||||
command_processor_.PushDebugMarker(
|
||||
"Mip Generation 3D: %ux%ux%u levels 1-%u",
|
||||
width * texture_resolution_scale_x,
|
||||
height * texture_resolution_scale_y, depth, level_last_for_mip_gen);
|
||||
} else {
|
||||
command_processor_.PushDebugMarker("Mip Generation: %ux%u levels 1-%u",
|
||||
command_processor_.PushDebugMarker("Mip Generation: %ux%ux%u levels 1-%u",
|
||||
width * texture_resolution_scale_x,
|
||||
height * texture_resolution_scale_y,
|
||||
level_last_for_mip_gen);
|
||||
array_size, level_last_for_mip_gen);
|
||||
}
|
||||
|
||||
ID3D12Resource* texture_resource = d3d12_texture.resource();
|
||||
DXGI_FORMAT texture_dxgi_format = GetDXGIResourceFormat(texture_key);
|
||||
// Use the correct format based on texture signedness.
|
||||
DXGI_FORMAT texture_dxgi_format =
|
||||
texture_key.signed_separate
|
||||
? host_formats_[uint32_t(texture_key.format)].dxgi_format_signed
|
||||
: GetDXGIUnormFormat(texture_key);
|
||||
|
||||
// Check if the format supports typed UAV stores for mip generation.
|
||||
// Note: SNORM formats don't support typed UAV stores, so signed textures
|
||||
// will skip mip generation.
|
||||
if (!FormatSupportsMipGenerationUAV(texture_dxgi_format)) {
|
||||
XELOGW(
|
||||
"Skipping mip generation for scaled resolve texture: DXGI format %u "
|
||||
@@ -1966,6 +1960,7 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
command_list.D3DSetComputeRootSignature(mip_gen_root_signature_.Get());
|
||||
|
||||
// Generate each mip level by downsampling from the previous level.
|
||||
// For texture arrays, process each slice separately.
|
||||
for (uint32_t level = 1; level <= level_last_for_mip_gen; ++level) {
|
||||
uint32_t src_width = std::max(scaled_width >> (level - 1), UINT32_C(1));
|
||||
uint32_t src_height = std::max(scaled_height >> (level - 1), UINT32_C(1));
|
||||
@@ -1974,38 +1969,40 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
uint32_t dst_height = std::max(scaled_height >> level, UINT32_C(1));
|
||||
uint32_t dst_depth = std::max(depth >> level, UINT32_C(1));
|
||||
|
||||
// Transition source mip (level - 1) to pixel shader resource for reading.
|
||||
// Transition destination mip (level) to unordered access for writing.
|
||||
// Note: For first iteration, source (level 0) is in COPY_DEST state from
|
||||
// the buffer copy above.
|
||||
D3D12_RESOURCE_BARRIER barriers[2];
|
||||
uint32_t barrier_count = 0;
|
||||
// Transition source and destination mips for all array slices.
|
||||
// Subresource index = mip + slice * mip_count.
|
||||
std::vector<D3D12_RESOURCE_BARRIER> barriers;
|
||||
barriers.reserve(array_size * 2);
|
||||
for (uint32_t slice = 0; slice < array_size; ++slice) {
|
||||
uint32_t src_subresource = (level - 1) + slice * texture_level_count;
|
||||
uint32_t dst_subresource = level + slice * texture_level_count;
|
||||
|
||||
// Source barrier - from COPY_DEST (level 0) or UAV (level > 0) to SRV.
|
||||
// Subresource index = MipSlice for ArraySlice=0, PlaneSlice=0.
|
||||
barriers[barrier_count].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barriers[barrier_count].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barriers[barrier_count].Transition.pResource = texture_resource;
|
||||
barriers[barrier_count].Transition.Subresource = level - 1;
|
||||
barriers[barrier_count].Transition.StateBefore =
|
||||
(level == 1) ? D3D12_RESOURCE_STATE_COPY_DEST
|
||||
: D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
barriers[barrier_count].Transition.StateAfter =
|
||||
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
++barrier_count;
|
||||
// Source barrier - from COPY_DEST (level 0) or UAV (level > 0) to SRV.
|
||||
D3D12_RESOURCE_BARRIER src_barrier;
|
||||
src_barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
src_barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
src_barrier.Transition.pResource = texture_resource;
|
||||
src_barrier.Transition.Subresource = src_subresource;
|
||||
src_barrier.Transition.StateBefore =
|
||||
(level == 1) ? D3D12_RESOURCE_STATE_COPY_DEST
|
||||
: D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
src_barrier.Transition.StateAfter =
|
||||
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
barriers.push_back(src_barrier);
|
||||
|
||||
// Destination barrier - from COPY_DEST to UAV.
|
||||
barriers[barrier_count].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barriers[barrier_count].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barriers[barrier_count].Transition.pResource = texture_resource;
|
||||
barriers[barrier_count].Transition.Subresource = level;
|
||||
barriers[barrier_count].Transition.StateBefore =
|
||||
D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barriers[barrier_count].Transition.StateAfter =
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
++barrier_count;
|
||||
|
||||
command_list.D3DResourceBarrier(barrier_count, barriers);
|
||||
// Destination barrier - from COPY_DEST to UAV.
|
||||
D3D12_RESOURCE_BARRIER dst_barrier;
|
||||
dst_barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
dst_barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
dst_barrier.Transition.pResource = texture_resource;
|
||||
dst_barrier.Transition.Subresource = dst_subresource;
|
||||
dst_barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
dst_barrier.Transition.StateAfter =
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
barriers.push_back(dst_barrier);
|
||||
}
|
||||
command_list.D3DResourceBarrier(uint32_t(barriers.size()),
|
||||
barriers.data());
|
||||
|
||||
// Allocate descriptors for SRV and UAV.
|
||||
ui::d3d12::util::DescriptorCpuGpuHandlePair mip_gen_descriptors[2];
|
||||
@@ -2019,6 +2016,7 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
}
|
||||
|
||||
// Create SRV for source mip level.
|
||||
// For 2D textures, use array view to process all slices in one dispatch.
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
||||
srv_desc.Format = texture_dxgi_format;
|
||||
srv_desc.Shader4ComponentMapping =
|
||||
@@ -2029,11 +2027,13 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
srv_desc.Texture3D.MipLevels = 1;
|
||||
srv_desc.Texture3D.ResourceMinLODClamp = 0.0f;
|
||||
} else {
|
||||
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MostDetailedMip = level - 1;
|
||||
srv_desc.Texture2D.MipLevels = 1;
|
||||
srv_desc.Texture2D.PlaneSlice = 0;
|
||||
srv_desc.Texture2D.ResourceMinLODClamp = 0.0f;
|
||||
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
srv_desc.Texture2DArray.MostDetailedMip = level - 1;
|
||||
srv_desc.Texture2DArray.MipLevels = 1;
|
||||
srv_desc.Texture2DArray.FirstArraySlice = 0;
|
||||
srv_desc.Texture2DArray.ArraySize = array_size;
|
||||
srv_desc.Texture2DArray.PlaneSlice = 0;
|
||||
srv_desc.Texture2DArray.ResourceMinLODClamp = 0.0f;
|
||||
}
|
||||
device->CreateShaderResourceView(texture_resource, &srv_desc,
|
||||
mip_gen_descriptors[0].first);
|
||||
@@ -2047,9 +2047,11 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
uav_desc.Texture3D.FirstWSlice = 0;
|
||||
uav_desc.Texture3D.WSize = dst_depth;
|
||||
} else {
|
||||
uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
uav_desc.Texture2D.MipSlice = level;
|
||||
uav_desc.Texture2D.PlaneSlice = 0;
|
||||
uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
||||
uav_desc.Texture2DArray.MipSlice = level;
|
||||
uav_desc.Texture2DArray.FirstArraySlice = 0;
|
||||
uav_desc.Texture2DArray.ArraySize = array_size;
|
||||
uav_desc.Texture2DArray.PlaneSlice = 0;
|
||||
}
|
||||
device->CreateUnorderedAccessView(texture_resource, nullptr, &uav_desc,
|
||||
mip_gen_descriptors[1].first);
|
||||
@@ -2076,7 +2078,7 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
2, mip_gen_descriptors[1].second);
|
||||
|
||||
// Dispatch compute shader.
|
||||
// 2D: Thread group size is 8x8.
|
||||
// 2D: Thread group size is 8x8, dispatch z = array_size for all slices.
|
||||
// 3D: Thread group size is 4x4x4.
|
||||
if (is_3d) {
|
||||
uint32_t group_count_x = (dst_width + 3) / 4;
|
||||
@@ -2086,7 +2088,7 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
} else {
|
||||
uint32_t group_count_x = (dst_width + 7) / 8;
|
||||
uint32_t group_count_y = (dst_height + 7) / 8;
|
||||
command_list.D3DDispatch(group_count_x, group_count_y, 1);
|
||||
command_list.D3DDispatch(group_count_x, group_count_y, array_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2094,27 +2096,28 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
// texture's expected state after loading.
|
||||
// Level 0 to level_last_for_mip_gen - 1 are in NON_PIXEL_SHADER_RESOURCE.
|
||||
// Level level_last_for_mip_gen is in UNORDERED_ACCESS.
|
||||
// Subresource index = MipSlice for ArraySlice=0, PlaneSlice=0.
|
||||
// Subresource index = mip + slice * mip_count.
|
||||
std::vector<D3D12_RESOURCE_BARRIER> final_barriers;
|
||||
final_barriers.reserve(level_last_for_mip_gen + 1);
|
||||
for (uint32_t level = 0; level < level_last_for_mip_gen; ++level) {
|
||||
final_barriers.reserve((level_last_for_mip_gen + 1) * array_size);
|
||||
for (uint32_t slice = 0; slice < array_size; ++slice) {
|
||||
for (uint32_t level = 0; level < level_last_for_mip_gen; ++level) {
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = texture_resource;
|
||||
barrier.Transition.Subresource = level + slice * texture_level_count;
|
||||
barrier.Transition.StateBefore =
|
||||
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
final_barriers.push_back(barrier);
|
||||
}
|
||||
// Last mip level is in UAV state.
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = texture_resource;
|
||||
barrier.Transition.Subresource = level;
|
||||
barrier.Transition.StateBefore =
|
||||
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
final_barriers.push_back(barrier);
|
||||
}
|
||||
// Last mip level is in UAV state.
|
||||
{
|
||||
D3D12_RESOURCE_BARRIER barrier;
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = texture_resource;
|
||||
barrier.Transition.Subresource = level_last_for_mip_gen;
|
||||
barrier.Transition.Subresource =
|
||||
level_last_for_mip_gen + slice * texture_level_count;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
final_barriers.push_back(barrier);
|
||||
|
||||
+26
-26
@@ -18,32 +18,32 @@ cs_5_1
|
||||
dcl_globalFlags refactoringAllowed | allResourcesBound
|
||||
dcl_constantbuffer CB0[0:0][1], immediateIndexed, space=0
|
||||
dcl_sampler S0[0:0], mode_default, space=0
|
||||
dcl_resource_texture2d (float,float,float,float) T0[0:0], space=0
|
||||
dcl_uav_typed_texture2d (float,float,float,float) U0[0:0], space=0
|
||||
dcl_input vThreadID.xy
|
||||
dcl_temps 1
|
||||
dcl_resource_texture2darray (float,float,float,float) T0[0:0], space=0
|
||||
dcl_uav_typed_texture2darray (float,float,float,float) U0[0:0], space=0
|
||||
dcl_input vThreadID.xyz
|
||||
dcl_temps 2
|
||||
dcl_thread_group 8, 8, 1
|
||||
uge r0.xy, vThreadID.xyxx, CB0[0][0].zwzz
|
||||
or r0.x, r0.y, r0.x
|
||||
if_nz r0.x
|
||||
ret
|
||||
endif
|
||||
utof r0.xy, vThreadID.xyxx
|
||||
add r0.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
|
||||
utof r0.zw, CB0[0][0].zzzw
|
||||
div r0.xy, r0.xyxx, r0.zwzz
|
||||
sample_l r0.xyzw, r0.xyxx, T0[0].xyzw, S0[0], l(0.000000)
|
||||
store_uav_typed U0[0].xyzw, vThreadID.xyyy, r0.xyzw
|
||||
utof r0.xyz, vThreadID.xyzx
|
||||
add r1.xy, r0.xyxx, l(0.500000, 0.500000, 0.000000, 0.000000)
|
||||
utof r1.zw, CB0[0][0].zzzw
|
||||
div r0.xy, r1.xyxx, r1.zwzz
|
||||
sample_l r0.xyzw, r0.xyzx, T0[0].xyzw, S0[0], l(0.000000)
|
||||
store_uav_typed U0[0].xyzw, vThreadID.xyzz, r0.xyzw
|
||||
ret
|
||||
// Approximately 0 instruction slots used
|
||||
#endif
|
||||
|
||||
const BYTE mip_generate_cs[] =
|
||||
{
|
||||
68, 88, 66, 67, 105, 246,
|
||||
102, 79, 76, 251, 37, 172,
|
||||
110, 28, 51, 60, 201, 185,
|
||||
141, 14, 1, 0, 0, 0,
|
||||
68, 88, 66, 67, 59, 97,
|
||||
167, 140, 139, 19, 29, 32,
|
||||
0, 105, 17, 219, 209, 230,
|
||||
254, 36, 1, 0, 0, 0,
|
||||
0, 2, 0, 0, 3, 0,
|
||||
0, 0, 44, 0, 0, 0,
|
||||
60, 0, 0, 0, 76, 0,
|
||||
@@ -64,19 +64,19 @@ const BYTE mip_generate_cs[] =
|
||||
0, 6, 70, 110, 48, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 88, 24,
|
||||
0, 0, 0, 0, 88, 64,
|
||||
0, 7, 70, 126, 48, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
85, 85, 0, 0, 0, 0,
|
||||
0, 0, 156, 24, 0, 7,
|
||||
0, 0, 156, 64, 0, 7,
|
||||
70, 238, 49, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 85, 85,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
95, 0, 0, 2, 50, 0,
|
||||
95, 0, 0, 2, 114, 0,
|
||||
2, 0, 104, 0, 0, 2,
|
||||
1, 0, 0, 0, 155, 0,
|
||||
2, 0, 0, 0, 155, 0,
|
||||
0, 4, 8, 0, 0, 0,
|
||||
8, 0, 0, 0, 1, 0,
|
||||
0, 0, 80, 0, 0, 8,
|
||||
@@ -93,27 +93,27 @@ const BYTE mip_generate_cs[] =
|
||||
10, 0, 16, 0, 0, 0,
|
||||
0, 0, 62, 0, 0, 1,
|
||||
21, 0, 0, 1, 86, 0,
|
||||
0, 4, 50, 0, 16, 0,
|
||||
0, 0, 0, 0, 70, 0,
|
||||
0, 4, 114, 0, 16, 0,
|
||||
0, 0, 0, 0, 70, 2,
|
||||
2, 0, 0, 0, 0, 10,
|
||||
50, 0, 16, 0, 0, 0,
|
||||
50, 0, 16, 0, 1, 0,
|
||||
0, 0, 70, 0, 16, 0,
|
||||
0, 0, 0, 0, 2, 64,
|
||||
0, 0, 0, 0, 0, 63,
|
||||
0, 0, 0, 63, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
86, 0, 0, 7, 194, 0,
|
||||
16, 0, 0, 0, 0, 0,
|
||||
16, 0, 1, 0, 0, 0,
|
||||
166, 142, 48, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 14, 0,
|
||||
0, 7, 50, 0, 16, 0,
|
||||
0, 0, 0, 0, 70, 0,
|
||||
16, 0, 0, 0, 0, 0,
|
||||
230, 10, 16, 0, 0, 0,
|
||||
16, 0, 1, 0, 0, 0,
|
||||
230, 10, 16, 0, 1, 0,
|
||||
0, 0, 72, 0, 0, 13,
|
||||
242, 0, 16, 0, 0, 0,
|
||||
0, 0, 70, 0, 16, 0,
|
||||
0, 0, 70, 2, 16, 0,
|
||||
0, 0, 0, 0, 70, 126,
|
||||
32, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 96,
|
||||
@@ -122,7 +122,7 @@ const BYTE mip_generate_cs[] =
|
||||
0, 0, 0, 0, 0, 0,
|
||||
164, 0, 0, 7, 242, 224,
|
||||
33, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 70, 5,
|
||||
0, 0, 0, 0, 70, 10,
|
||||
2, 0, 70, 14, 16, 0,
|
||||
0, 0, 0, 0, 62, 0,
|
||||
0, 1
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
|
||||
// Mip generation compute shader for scaled resolve textures.
|
||||
// Generates a single mip level by downsampling from the previous level using
|
||||
// bilinear filtering.
|
||||
// bilinear filtering. Supports texture arrays via SV_DispatchThreadID.z.
|
||||
|
||||
cbuffer XeMipGenerateConstants : register(b0) {
|
||||
uint2 xe_mip_gen_source_size; // Source mip dimensions
|
||||
uint2 xe_mip_gen_dest_size; // Destination mip dimensions
|
||||
};
|
||||
|
||||
Texture2D<float4> xe_mip_gen_source : register(t0);
|
||||
RWTexture2D<float4> xe_mip_gen_dest : register(u0);
|
||||
Texture2DArray<float4> xe_mip_gen_source : register(t0);
|
||||
RWTexture2DArray<float4> xe_mip_gen_dest : register(u0);
|
||||
SamplerState xe_sampler_linear_clamp : register(s0);
|
||||
|
||||
[numthreads(8, 8, 1)]
|
||||
@@ -29,9 +29,12 @@ void main(uint3 xe_thread_id : SV_DispatchThreadID) {
|
||||
|
||||
// Calculate UV coordinates for sampling the source texture.
|
||||
// Add 0.5 to sample at pixel center, then normalize to [0, 1] range.
|
||||
float2 uv = (float2(xe_thread_id.xy) + 0.5f) / float2(xe_mip_gen_dest_size);
|
||||
// xe_thread_id.z is the array slice index.
|
||||
float3 uvw = float3((float2(xe_thread_id.xy) + 0.5f) /
|
||||
float2(xe_mip_gen_dest_size),
|
||||
xe_thread_id.z);
|
||||
|
||||
// Sample with bilinear filtering and write to destination.
|
||||
xe_mip_gen_dest[xe_thread_id.xy] =
|
||||
xe_mip_gen_source.SampleLevel(xe_sampler_linear_clamp, uv, 0);
|
||||
xe_mip_gen_dest[xe_thread_id] =
|
||||
xe_mip_gen_source.SampleLevel(xe_sampler_linear_clamp, uvw, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user