mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
Merge remote-tracking branch 'xenia-master/master' into edge
This commit is contained in:
@@ -108,3 +108,6 @@
|
||||
[submodule "third_party/miniaudio"]
|
||||
path = third_party/miniaudio
|
||||
url = https://github.com/mackron/miniaudio.git
|
||||
[submodule "third_party/DirectX-Headers"]
|
||||
path = third_party/DirectX-Headers
|
||||
url = https://github.com/microsoft/DirectX-Headers.git
|
||||
|
||||
@@ -2877,7 +2877,6 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
if (host_render_targets_used) {
|
||||
bound_depth_and_color_render_target_bits =
|
||||
render_target_cache_->GetLastUpdateBoundRenderTargets(
|
||||
render_target_cache_->gamma_render_target_as_srgb(),
|
||||
bound_depth_and_color_render_target_formats);
|
||||
} else {
|
||||
bound_depth_and_color_render_target_bits = 0;
|
||||
@@ -3238,8 +3237,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
// Invalidate textures in memexported memory and watch for changes.
|
||||
for (const draw_util::MemExportRange& memexport_range : memexport_ranges_) {
|
||||
shared_memory_->RangeWrittenByGpu(
|
||||
memexport_range.base_address_dwords << 2, memexport_range.size_bytes,
|
||||
false);
|
||||
memexport_range.base_address_dwords << 2, memexport_range.size_bytes);
|
||||
}
|
||||
if (GetGPUSetting(GPUSetting::ReadbackMemexport)) {
|
||||
// Read the exported data on the CPU.
|
||||
@@ -4433,7 +4431,8 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
flags |= uint32_t(alpha_test_function)
|
||||
<< DxbcShaderTranslator::kSysFlag_AlphaPassIfLess_Shift;
|
||||
// Gamma writing.
|
||||
if (!render_target_cache_->gamma_render_target_as_srgb()) {
|
||||
if (!(edram_rov_used ||
|
||||
render_target_cache_->gamma_render_target_as_unorm16())) {
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (color_infos[i].color_format ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
@@ -4600,9 +4599,7 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
}
|
||||
|
||||
// Texture signedness / gamma.
|
||||
bool gamma_render_target_as_srgb =
|
||||
render_target_cache_->gamma_render_target_as_srgb();
|
||||
uint32_t textures_resolved = 0;
|
||||
uint32_t textures_resolution_scaled = 0;
|
||||
uint32_t textures_remaining = used_texture_mask;
|
||||
uint32_t texture_index;
|
||||
while (xe::bit_scan_forward(textures_remaining, &texture_index)) {
|
||||
@@ -4621,15 +4618,14 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
|
||||
texture_signs_uint =
|
||||
(texture_signs_uint & ~texture_signs_mask) | texture_signs_shifted;
|
||||
// cache misses here, we're accessing the texture bindings out of order
|
||||
textures_resolved |=
|
||||
uint32_t(texture_cache_->IsActiveTextureResolved(texture_index))
|
||||
textures_resolution_scaled |=
|
||||
uint32_t(texture_cache_->IsActiveTextureResolutionScaled(texture_index))
|
||||
<< texture_index;
|
||||
}
|
||||
|
||||
update_dirty_uint32_cmp(system_constants_.textures_resolved,
|
||||
textures_resolved);
|
||||
system_constants_.textures_resolved = textures_resolved;
|
||||
update_dirty_uint32_cmp(system_constants_.textures_resolution_scaled,
|
||||
textures_resolution_scaled);
|
||||
system_constants_.textures_resolution_scaled = textures_resolution_scaled;
|
||||
|
||||
// Log2 of sample count, for alpha to mask and with ROV, for EDRAM address
|
||||
// calculation with MSAA.
|
||||
|
||||
@@ -429,7 +429,7 @@ bool D3D12RenderTargetCache::Initialize() {
|
||||
if (path_ == Path::kHostRenderTargets) {
|
||||
// Host render targets.
|
||||
|
||||
gamma_render_target_as_srgb_ = cvars::gamma_render_target_as_srgb;
|
||||
gamma_render_target_as_unorm16_ = cvars::gamma_render_target_as_unorm16;
|
||||
|
||||
depth_float24_round_ = cvars::depth_float24_round;
|
||||
depth_float24_convert_in_pixel_shader_ =
|
||||
@@ -472,6 +472,17 @@ bool D3D12RenderTargetCache::Initialize() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (msaa_2x_supported_ && gamma_render_target_as_unorm16_) {
|
||||
multisample_quality_levels.Format = DXGI_FORMAT_R16G16B16A16_UNORM;
|
||||
multisample_quality_levels.NumQualityLevels = 0;
|
||||
if (FAILED(device->CheckFeatureSupport(
|
||||
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
|
||||
&multisample_quality_levels,
|
||||
sizeof(multisample_quality_levels))) ||
|
||||
!multisample_quality_levels.NumQualityLevels) {
|
||||
msaa_2x_supported_ = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msaa_2x_supported_ = false;
|
||||
}
|
||||
@@ -987,8 +998,8 @@ bool D3D12RenderTargetCache::Initialize() {
|
||||
} else if (path_ == Path::kPixelShaderInterlock) {
|
||||
// Pixel shader interlock (rasterizer-ordered view).
|
||||
|
||||
// Blending is done in linear space directly in shaders.
|
||||
gamma_render_target_as_srgb_ = false;
|
||||
// Piecewise linear gamma is 8-bit with programmable blending.
|
||||
gamma_render_target_as_unorm16_ = false;
|
||||
|
||||
// Always true float24 depth rounded to the nearest even.
|
||||
depth_float24_round_ = true;
|
||||
@@ -1762,12 +1773,10 @@ DXGI_FORMAT D3D12RenderTargetCache::GetColorResourceDXGIFormat(
|
||||
// compression.
|
||||
switch (format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
if (gamma_render_target_as_srgb_) {
|
||||
// Can toggle between UNORM and UNORM_SRGB for the same data.
|
||||
return DXGI_FORMAT_R8G8B8A8_TYPELESS;
|
||||
}
|
||||
return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
return gamma_render_target_as_unorm16_ ? DXGI_FORMAT_R16G16B16A16_UNORM
|
||||
: DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10:
|
||||
return DXGI_FORMAT_R10G10B10A2_UNORM;
|
||||
@@ -1800,11 +1809,6 @@ DXGI_FORMAT D3D12RenderTargetCache::GetColorResourceDXGIFormat(
|
||||
DXGI_FORMAT D3D12RenderTargetCache::GetColorDrawDXGIFormat(
|
||||
xenos::ColorRenderTargetFormat format) const {
|
||||
switch (format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
return gamma_render_target_as_srgb_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
|
||||
: DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
case xenos::ColorRenderTargetFormat::k_16_16:
|
||||
return DXGI_FORMAT_R16G16_SNORM;
|
||||
case xenos::ColorRenderTargetFormat::k_16_16_16_16:
|
||||
@@ -1898,6 +1902,10 @@ DXGI_FORMAT D3D12RenderTargetCache::GetDepthSRVStencilDXGIFormat(
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D12RenderTargetCache::IsGammaFormatHostStorageSeparate() const {
|
||||
return gamma_render_target_as_unorm16_;
|
||||
}
|
||||
|
||||
RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
|
||||
RenderTargetKey key) {
|
||||
ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice();
|
||||
@@ -1978,7 +1986,6 @@ RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
|
||||
}
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE descriptor_draw_handle =
|
||||
descriptor_draw.GetHandle();
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_srgb;
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_load_separate;
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_srv_stencil;
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
|
||||
@@ -2037,23 +2044,6 @@ RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
|
||||
}
|
||||
device->CreateRenderTargetView(resource.Get(), &rtv_desc,
|
||||
descriptor_draw_handle);
|
||||
// sRGB drawing RTV.
|
||||
switch (key.GetColorFormat()) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
if (gamma_render_target_as_srgb_) {
|
||||
descriptor_draw_srgb = descriptor_pool.AllocateDescriptor();
|
||||
if (!descriptor_draw_srgb.IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
device->CreateRenderTargetView(resource.Get(), &rtv_desc,
|
||||
descriptor_draw_srgb.GetHandle());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Ownership transfer RTV.
|
||||
DXGI_FORMAT load_format =
|
||||
GetColorOwnershipTransferDXGIFormat(key.GetColorFormat());
|
||||
@@ -2074,9 +2064,8 @@ RenderTargetCache::RenderTarget* D3D12RenderTargetCache::CreateRenderTarget(
|
||||
|
||||
return new D3D12RenderTarget(
|
||||
key, resource.Get(), std::move(descriptor_draw),
|
||||
std::move(descriptor_draw_srgb), std::move(descriptor_load_separate),
|
||||
std::move(descriptor_srv), std::move(descriptor_srv_stencil),
|
||||
resource_state);
|
||||
std::move(descriptor_load_separate), std::move(descriptor_srv),
|
||||
std::move(descriptor_srv_stencil), resource_state);
|
||||
}
|
||||
|
||||
bool D3D12RenderTargetCache::IsHostDepthEncodingDifferent(
|
||||
@@ -3417,15 +3406,25 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
|
||||
if (dest_is_64bpp) {
|
||||
// Handle construction of 64bpp color, either from two 32-bit samples in r0
|
||||
// and r1, or from one 64bpp sample in r1. Using r2.x as temporary when
|
||||
// and r1, or from one 64bpp sample in r1. Using r2.xy as temporary when
|
||||
// needed.
|
||||
// If color_packed_in_r0x_and_r1x, use the generic path for combining two
|
||||
// 32-bit samples - as raw in r0.x and r1.x - into the destination.
|
||||
bool color_packed_in_r0x_and_r1x = false;
|
||||
if (source_is_color) {
|
||||
switch (source_color_format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
// 8_8_8_8_GAMMA is represented by linear stored in
|
||||
// R16G16B16A16_UNORM.
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
for (uint32_t j = 0; j < 3; ++j) {
|
||||
DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(a, i, j, i, j,
|
||||
2, 0, 2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
[[fallthrough]];
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8: {
|
||||
color_packed_in_r0x_and_r1x = true;
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a.OpMAd(dxbc::Dest::R(i), dxbc::Src::R(i), dxbc::Src::LF(255.0f),
|
||||
@@ -3576,7 +3575,14 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
switch (source_color_format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
// 8_8_8_8_GAMMA is represented by linear stored in
|
||||
// R16G16B16A16_UNORM.
|
||||
if (dest_is_stencil_bit) {
|
||||
if (source_color_format ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(a, 1, 0, 1, 0,
|
||||
2, 0, 2, 1);
|
||||
}
|
||||
a.OpMAd(dxbc::Dest::R(1, 0b0001), dxbc::Src::R(1, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(255.0f), dxbc::Src::LF(0.5f));
|
||||
a.OpFToU(dxbc::Dest::R(1, 0b0001),
|
||||
@@ -3586,9 +3592,30 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8 ||
|
||||
dest_color_format ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA)) {
|
||||
// Same format - passthrough.
|
||||
// Same format - only perform color space conversion.
|
||||
if (dest_color_format != source_color_format) {
|
||||
if (dest_color_format ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8) {
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(
|
||||
a, 1, i, 1, i, 2, 0, 2, 1);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
DxbcShaderTranslator::PWLGammaToLinear(a, 1, i, 1, i, true, 2,
|
||||
0, 2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
a.OpMov(dxbc::Dest::O(0), dxbc::Src::R(1));
|
||||
} else if (mode.output == TransferOutput::kDepth) {
|
||||
if (source_color_format ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
for (uint32_t i = 1; i < 3; ++i) {
|
||||
DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(
|
||||
a, 1, i, 1, i, 2, 0, 2, 1);
|
||||
}
|
||||
}
|
||||
// When need only depth, not stencil, skip the red component.
|
||||
a.OpMAd(dxbc::Dest::R(
|
||||
1, osgn_parameter_index_sv_stencil_ref != UINT32_MAX
|
||||
@@ -3613,6 +3640,13 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
dxbc::Src::R(1, dxbc::Src::kYYYY));
|
||||
} else {
|
||||
color_packed_in_r1x = true;
|
||||
if (source_color_format ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(
|
||||
a, 1, i, 1, i, 2, 0, 2, 1);
|
||||
}
|
||||
}
|
||||
a.OpMAd(dxbc::Dest::R(1), dxbc::Src::R(1), dxbc::Src::LF(255.0f),
|
||||
dxbc::Src::LF(0.5f));
|
||||
a.OpFToU(dxbc::Dest::R(1), dxbc::Src::R(1));
|
||||
@@ -3756,8 +3790,7 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
// this is the end of the shader.
|
||||
if (color_packed_in_r1x) {
|
||||
switch (dest_color_format) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8: {
|
||||
a.OpUBFE(dxbc::Dest::R(1), dxbc::Src::LU(8),
|
||||
dxbc::Src::LU(0, 8, 16, 24),
|
||||
dxbc::Src::R(1, dxbc::Src::kXXXX));
|
||||
@@ -3765,6 +3798,26 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
a.OpMul(dxbc::Dest::O(0), dxbc::Src::R(1),
|
||||
dxbc::Src::LF(1.0f / 255.0f));
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
// 8_8_8_8_GAMMA is represented by linear stored in
|
||||
// R16G16B16A16_UNORM.
|
||||
a.OpUBFE(dxbc::Dest::R(1), dxbc::Src::LU(8),
|
||||
dxbc::Src::LU(0, 8, 16, 24),
|
||||
dxbc::Src::R(1, dxbc::Src::kXXXX));
|
||||
a.OpUToF(dxbc::Dest::R(1), dxbc::Src::R(1));
|
||||
a.OpMul(dxbc::Dest::R(1, 0b0111), dxbc::Src::R(1),
|
||||
dxbc::Src::LF(1.0f / 255.0f));
|
||||
a.OpMul(dxbc::Dest::O(0, 0b1000), dxbc::Src::R(1),
|
||||
dxbc::Src::LF(1.0f / 255.0f));
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
DxbcShaderTranslator::PWLGammaToLinear(a, 1, i, 1, i, true, 0,
|
||||
0, 0, 1);
|
||||
}
|
||||
// TODO(Triang3l): The `mov` can be eliminated by passing the
|
||||
// destination to `PWLGammaToLinear` as `dxbc::Dest` rather than
|
||||
// just the register index.
|
||||
a.OpMov(dxbc::Dest::O(0, 0b0111), dxbc::Src::R(1));
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10: {
|
||||
a.OpUBFE(dxbc::Dest::R(1), dxbc::Src::LU(10, 10, 10, 2),
|
||||
@@ -5360,13 +5413,24 @@ void D3D12RenderTargetCache::PerformTransfersAndResolveClears(
|
||||
float color_clear_value[4] = {};
|
||||
bool clear_via_drawing = false;
|
||||
switch (dest_rt_key.GetColorFormat()) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8: {
|
||||
for (uint32_t j = 0; j < 4; ++j) {
|
||||
color_clear_value[j] =
|
||||
((clear_value >> (j * 8)) & 0xFF) * (1.0f / 0xFF);
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA: {
|
||||
// 8_8_8_8_GAMMA is represented by linear stored in
|
||||
// R16G16B16A16_UNORM.
|
||||
for (uint32_t j = 0; j < 4; ++j) {
|
||||
color_clear_value[j] =
|
||||
((clear_value >> (j * 8)) & 0xFF) * (1.0f / 0xFF);
|
||||
}
|
||||
for (uint32_t j = 0; j < 3; ++j) {
|
||||
color_clear_value[j] =
|
||||
xenos::PWLGammaToLinear(color_clear_value[j]);
|
||||
}
|
||||
} break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10: {
|
||||
for (uint32_t j = 0; j < 3; ++j) {
|
||||
@@ -5513,17 +5577,6 @@ void D3D12RenderTargetCache::SetCommandListRenderTargets(
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t render_targets_are_srgb;
|
||||
if (gamma_render_target_as_srgb_) {
|
||||
render_targets_are_srgb = last_update_accumulated_color_targets_are_gamma();
|
||||
if (are_current_command_list_render_targets_srgb_ !=
|
||||
render_targets_are_srgb) {
|
||||
are_current_command_list_render_targets_srgb_ = render_targets_are_srgb;
|
||||
are_current_command_list_render_targets_valid_ = false;
|
||||
}
|
||||
} else {
|
||||
render_targets_are_srgb = 0;
|
||||
}
|
||||
if (!are_current_command_list_render_targets_valid_) {
|
||||
std::memcpy(current_command_list_render_targets_,
|
||||
depth_and_color_render_targets,
|
||||
@@ -5550,10 +5603,7 @@ void D3D12RenderTargetCache::SetCommandListRenderTargets(
|
||||
: null_rtv_descriptor_ss_.GetHandle();
|
||||
}
|
||||
auto& d3d12_rt = *static_cast<const D3D12RenderTarget*>(render_target);
|
||||
rtv_handles[rtv_count++] =
|
||||
(render_targets_are_srgb & (uint32_t(1) << i))
|
||||
? d3d12_rt.descriptor_draw_srgb().GetHandle()
|
||||
: d3d12_rt.descriptor_draw().GetHandle();
|
||||
rtv_handles[rtv_count++] = d3d12_rt.descriptor_draw().GetHandle();
|
||||
}
|
||||
command_processor_.GetDeferredCommandList().D3DOMSetRenderTargets(
|
||||
rtv_count, rtv_handles, false,
|
||||
@@ -6240,7 +6290,6 @@ ID3D12PipelineState* D3D12RenderTargetCache::GetOrCreateDumpPipeline(
|
||||
} else {
|
||||
switch (key.GetColorFormat()) {
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8:
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
if (!source_is_uint) {
|
||||
a.OpMAd(dxbc::Dest::R(1), dxbc::Src::R(1), dxbc::Src::LF(255.0f),
|
||||
dxbc::Src::LF(0.5f));
|
||||
@@ -6252,6 +6301,22 @@ ID3D12PipelineState* D3D12RenderTargetCache::GetOrCreateDumpPipeline(
|
||||
dxbc::Src::R(1, dxbc::Src::kXXXX));
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
// 8_8_8_8_GAMMA is represented by linear stored in R16G16B16A16_UNORM.
|
||||
assert_false(source_is_uint);
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(a, 1, i, 1, i, 0,
|
||||
0, 0, 1);
|
||||
}
|
||||
a.OpMAd(dxbc::Dest::R(1), dxbc::Src::R(1), dxbc::Src::LF(255.0f),
|
||||
dxbc::Src::LF(0.5f));
|
||||
a.OpFToU(dxbc::Dest::R(1), dxbc::Src::R(1));
|
||||
for (uint32_t i = 1; i < 4; ++i) {
|
||||
a.OpBFI(dxbc::Dest::R(1, 0b0001), dxbc::Src::LU(8),
|
||||
dxbc::Src::LU(i * 8), dxbc::Src::R(1).Select(i),
|
||||
dxbc::Src::R(1, dxbc::Src::kXXXX));
|
||||
}
|
||||
break;
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10:
|
||||
case xenos::ColorRenderTargetFormat::k_2_10_10_10_AS_10_10_10_10:
|
||||
if (!source_is_uint) {
|
||||
|
||||
@@ -95,8 +95,8 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
|
||||
// For host render targets.
|
||||
|
||||
bool gamma_render_target_as_srgb() const {
|
||||
return gamma_render_target_as_srgb_;
|
||||
bool gamma_render_target_as_unorm16() const {
|
||||
return gamma_render_target_as_unorm16_;
|
||||
}
|
||||
|
||||
// Using R16G16[B16A16]_SNORM, which are -1...1, not the needed -32...32.
|
||||
@@ -129,6 +129,8 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
xenos::DepthRenderTargetFormat format);
|
||||
|
||||
protected:
|
||||
bool IsGammaFormatHostStorageSeparate() const override;
|
||||
|
||||
uint32_t GetMaxRenderTargetWidth() const override {
|
||||
return D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
}
|
||||
@@ -225,16 +227,13 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
|
||||
class D3D12RenderTarget final : public RenderTarget {
|
||||
public:
|
||||
// descriptor_draw_srgb is only used for k_8_8_8_8 render targets when host
|
||||
// sRGB (gamma_render_target_as_srgb) is used. descriptor_load is present
|
||||
// when the DXGI formats are different for drawing and bit-exact loading
|
||||
// (for NaN pattern preservation across EDRAM tile ownership transfers in
|
||||
// floating-point formats, and to distinguish between two -1 representations
|
||||
// in snorm formats).
|
||||
// descriptor_load is present when the DXGI formats are different for
|
||||
// drawing and bit-exact loading (for NaN pattern preservation across EDRAM
|
||||
// tile ownership transfers in floating-point formats, and to distinguish
|
||||
// between two -1 representations in snorm formats).
|
||||
D3D12RenderTarget(
|
||||
RenderTargetKey key, ID3D12Resource* resource,
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_draw,
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_draw_srgb,
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&&
|
||||
descriptor_load_separate,
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor&& descriptor_srv,
|
||||
@@ -243,7 +242,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
: RenderTarget(key),
|
||||
resource_(resource),
|
||||
descriptor_draw_(std::move(descriptor_draw)),
|
||||
descriptor_draw_srgb_(std::move(descriptor_draw_srgb)),
|
||||
descriptor_load_separate_(std::move(descriptor_load_separate)),
|
||||
descriptor_srv_(std::move(descriptor_srv)),
|
||||
descriptor_srv_stencil_(std::move(descriptor_srv_stencil)),
|
||||
@@ -254,10 +252,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
const {
|
||||
return descriptor_draw_;
|
||||
}
|
||||
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor& descriptor_draw_srgb()
|
||||
const {
|
||||
return descriptor_draw_srgb_;
|
||||
}
|
||||
const ui::d3d12::D3D12CpuDescriptorPool::Descriptor& descriptor_srv()
|
||||
const {
|
||||
return descriptor_srv_;
|
||||
@@ -297,7 +291,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> resource_;
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_;
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_draw_srgb_;
|
||||
ui::d3d12::D3D12CpuDescriptorPool::Descriptor descriptor_load_separate_;
|
||||
// Texture SRV non-shader-visible descriptors, to prepare shader-visible
|
||||
// descriptors faster, by copying rather than by creating every time.
|
||||
@@ -718,7 +711,7 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
|
||||
bool use_stencil_reference_output_ = false;
|
||||
|
||||
bool gamma_render_target_as_srgb_ = false;
|
||||
bool gamma_render_target_as_unorm16_ = false;
|
||||
|
||||
bool depth_float24_round_ = false;
|
||||
bool depth_float24_convert_in_pixel_shader_ = false;
|
||||
@@ -753,7 +746,6 @@ class D3D12RenderTargetCache final : public RenderTargetCache {
|
||||
|
||||
const RenderTarget* const*
|
||||
current_command_list_render_targets_[1 + xenos::kMaxColorRenderTargets];
|
||||
uint32_t are_current_command_list_render_targets_srgb_ = 0;
|
||||
bool are_current_command_list_render_targets_valid_ = false;
|
||||
|
||||
// Temporary storage for descriptors used in PerformTransfersAndResolveClears
|
||||
|
||||
@@ -461,7 +461,7 @@ bool D3D12SharedMemory::UploadRanges(
|
||||
return false;
|
||||
}
|
||||
MakeRangeValid(upload_range_start << page_size_log2(),
|
||||
uint32_t(upload_buffer_size), false, false);
|
||||
uint32_t(upload_buffer_size), false);
|
||||
|
||||
if (upload_buffer_size < (1ULL << 32) && upload_buffer_size > 8192) {
|
||||
memory::vastcpy(
|
||||
@@ -469,9 +469,8 @@ bool D3D12SharedMemory::UploadRanges(
|
||||
memory().TranslatePhysical(upload_range_start << page_size_log2()),
|
||||
static_cast<uint32_t>(upload_buffer_size));
|
||||
swcache::WriteFence();
|
||||
|
||||
} else {
|
||||
memcpy(
|
||||
std::memcpy(
|
||||
upload_buffer_mapping,
|
||||
memory().TranslatePhysical(upload_range_start << page_size_log2()),
|
||||
upload_buffer_size);
|
||||
|
||||
@@ -96,7 +96,8 @@ PipelineCache::PipelineCache(D3D12CommandProcessor& command_processor,
|
||||
|
||||
shader_translator_ = std::make_unique<DxbcShaderTranslator>(
|
||||
provider.GetAdapterVendorID(), bindless_resources_used_, edram_rov_used,
|
||||
render_target_cache_.gamma_render_target_as_srgb(),
|
||||
!(edram_rov_used ||
|
||||
render_target_cache_.gamma_render_target_as_unorm16()),
|
||||
render_target_cache_.msaa_2x_supported(),
|
||||
render_target_cache_.draw_resolution_scale_x(),
|
||||
render_target_cache_.draw_resolution_scale_y(),
|
||||
@@ -1100,7 +1101,8 @@ void PipelineCache::TranslateShadersForStorage(
|
||||
StringBuffer ucode_disasm_buffer;
|
||||
DxbcShaderTranslator translator(
|
||||
provider.GetAdapterVendorID(), bindless_resources_used_, edram_rov_used,
|
||||
render_target_cache_.gamma_render_target_as_srgb(),
|
||||
!(edram_rov_used ||
|
||||
render_target_cache_.gamma_render_target_as_unorm16()),
|
||||
render_target_cache_.msaa_2x_supported(),
|
||||
render_target_cache_.draw_resolution_scale_x(),
|
||||
render_target_cache_.draw_resolution_scale_y(),
|
||||
@@ -3219,7 +3221,8 @@ void PipelineCache::CreationThread(size_t thread_index) {
|
||||
StringBuffer ucode_disasm_buffer;
|
||||
DxbcShaderTranslator translator(
|
||||
provider.GetAdapterVendorID(), bindless_resources_used_, edram_rov_used,
|
||||
render_target_cache_.gamma_render_target_as_srgb(),
|
||||
!(edram_rov_used ||
|
||||
render_target_cache_.gamma_render_target_as_unorm16()),
|
||||
render_target_cache_.msaa_2x_supported(),
|
||||
render_target_cache_.draw_resolution_scale_x(),
|
||||
render_target_cache_.draw_resolution_scale_y(),
|
||||
|
||||
@@ -1135,12 +1135,12 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
|
||||
auto rb_copy_dest_pitch = regs.Get<reg::RB_COPY_DEST_PITCH>();
|
||||
const uint32_t copy_dest_pitch_aligned =
|
||||
xe::align(rb_copy_dest_pitch.copy_dest_pitch,
|
||||
texture_address::kStoragePitchHeightAlignmentElements);
|
||||
texture_address::kStoragePitchHeightAlignmentBlocks);
|
||||
info_out.copy_dest_coordinate_info.pitch_aligned_div_32 =
|
||||
copy_dest_pitch_aligned >> 5;
|
||||
const uint32_t copy_dest_height_aligned =
|
||||
xe::align(rb_copy_dest_pitch.copy_dest_height,
|
||||
texture_address::kStoragePitchHeightAlignmentElements);
|
||||
texture_address::kStoragePitchHeightAlignmentBlocks);
|
||||
info_out.copy_dest_coordinate_info.height_aligned_div_32 =
|
||||
copy_dest_height_aligned >> 5;
|
||||
const FormatInfo& dest_format_info = *FormatInfo::Get(dest_format);
|
||||
|
||||
@@ -65,7 +65,7 @@ using namespace ucode;
|
||||
|
||||
DxbcShaderTranslator::DxbcShaderTranslator(
|
||||
ui::GraphicsProvider::GpuVendorID vendor_id, bool bindless_resources_used,
|
||||
bool edram_rov_used, bool gamma_render_target_as_srgb,
|
||||
bool edram_rov_used, bool gamma_render_target_as_unorm8,
|
||||
bool msaa_2x_supported, uint32_t draw_resolution_scale_x,
|
||||
uint32_t draw_resolution_scale_y, bool force_emit_source_map)
|
||||
: a_(shader_code_, statistics_),
|
||||
@@ -73,7 +73,7 @@ DxbcShaderTranslator::DxbcShaderTranslator(
|
||||
vendor_id_(vendor_id),
|
||||
bindless_resources_used_(bindless_resources_used),
|
||||
edram_rov_used_(edram_rov_used),
|
||||
gamma_render_target_as_srgb_(gamma_render_target_as_srgb),
|
||||
gamma_render_target_as_unorm8_(gamma_render_target_as_unorm8),
|
||||
msaa_2x_supported_(msaa_2x_supported),
|
||||
draw_resolution_scale_x_(draw_resolution_scale_x),
|
||||
draw_resolution_scale_y_(draw_resolution_scale_y),
|
||||
@@ -222,9 +222,10 @@ void DxbcShaderTranslator::PopSystemTemp(uint32_t count) {
|
||||
}
|
||||
|
||||
void DxbcShaderTranslator::PWLGammaToLinear(
|
||||
uint32_t target_temp, uint32_t target_temp_component, uint32_t source_temp,
|
||||
uint32_t source_temp_component, bool source_pre_saturated, uint32_t temp1,
|
||||
uint32_t temp1_component, uint32_t temp2, uint32_t temp2_component) {
|
||||
dxbc::Assembler& a, uint32_t target_temp, uint32_t target_temp_component,
|
||||
uint32_t source_temp, uint32_t source_temp_component,
|
||||
bool source_pre_saturated, uint32_t temp1, uint32_t temp1_component,
|
||||
uint32_t temp2, uint32_t temp2_component) {
|
||||
// The source is needed only once to begin building the result, so it can be
|
||||
// the same as the destination.
|
||||
assert_true(temp1 != target_temp || temp1_component != target_temp_component);
|
||||
@@ -245,25 +246,25 @@ void DxbcShaderTranslator::PWLGammaToLinear(
|
||||
// Using `source >= threshold` comparisons because the input might have not
|
||||
// been saturated yet, and thus it may be NaN - since it will be saturated to
|
||||
// 0 later, the 0...64/255 case should be selected for it.
|
||||
a_.OpGE(temp2_dest, source_src, dxbc::Src::LF(96.0f / 255.0f));
|
||||
a_.OpIf(true, temp2_src);
|
||||
a.OpGE(temp2_dest, source_src, dxbc::Src::LF(96.0f / 255.0f));
|
||||
a.OpIf(true, temp2_src);
|
||||
// [96/255 ... 1
|
||||
a_.OpGE(temp2_dest, source_src, dxbc::Src::LF(192.0f / 255.0f));
|
||||
a_.OpMovC(temp1_dest, temp2_src, dxbc::Src::LF(8.0f / 1024.0f),
|
||||
dxbc::Src::LF(4.0f / 1024.0f));
|
||||
a_.OpMovC(temp2_dest, temp2_src, dxbc::Src::LF(-1024.0f),
|
||||
dxbc::Src::LF(-256.0f));
|
||||
a_.OpElse();
|
||||
a.OpGE(temp2_dest, source_src, dxbc::Src::LF(192.0f / 255.0f));
|
||||
a.OpMovC(temp1_dest, temp2_src, dxbc::Src::LF(8.0f / 1024.0f),
|
||||
dxbc::Src::LF(4.0f / 1024.0f));
|
||||
a.OpMovC(temp2_dest, temp2_src, dxbc::Src::LF(-1024.0f),
|
||||
dxbc::Src::LF(-256.0f));
|
||||
a.OpElse();
|
||||
// 0 ... 96/255)
|
||||
a_.OpGE(temp2_dest, source_src, dxbc::Src::LF(64.0f / 255.0f));
|
||||
a_.OpMovC(temp1_dest, temp2_src, dxbc::Src::LF(2.0f / 1024.0f),
|
||||
dxbc::Src::LF(1.0f / 1024.0f));
|
||||
a_.OpMovC(temp2_dest, temp2_src, dxbc::Src::LF(-64.0f), dxbc::Src::LF(0.0f));
|
||||
a_.OpEndIf();
|
||||
a.OpGE(temp2_dest, source_src, dxbc::Src::LF(64.0f / 255.0f));
|
||||
a.OpMovC(temp1_dest, temp2_src, dxbc::Src::LF(2.0f / 1024.0f),
|
||||
dxbc::Src::LF(1.0f / 1024.0f));
|
||||
a.OpMovC(temp2_dest, temp2_src, dxbc::Src::LF(-64.0f), dxbc::Src::LF(0.0f));
|
||||
a.OpEndIf();
|
||||
|
||||
if (!source_pre_saturated) {
|
||||
// Saturate the input, and flush NaN to 0.
|
||||
a_.OpMov(target_dest, source_src, true);
|
||||
a.OpMov(target_dest, source_src, true);
|
||||
}
|
||||
// linear = gamma * (255 * 1024) * scale + offset
|
||||
// As both 1024 and the scale are powers of 2, and 1024 * scale is not smaller
|
||||
@@ -272,22 +273,22 @@ void DxbcShaderTranslator::PWLGammaToLinear(
|
||||
// gamma * (255 * 1024 * scale) - or the option chosen here, as long as
|
||||
// 1024 is applied before the scale since the scale is < 1 (specifically at
|
||||
// least 1/1024), and it may make very small values denormal.
|
||||
a_.OpMul(target_dest, source_pre_saturated ? source_src : target_src,
|
||||
dxbc::Src::LF(255.0f * 1024.0f));
|
||||
a_.OpMAd(target_dest, target_src, temp1_src, temp2_src);
|
||||
a.OpMul(target_dest, source_pre_saturated ? source_src : target_src,
|
||||
dxbc::Src::LF(255.0f * 1024.0f));
|
||||
a.OpMAd(target_dest, target_src, temp1_src, temp2_src);
|
||||
// linear += trunc(linear * scale)
|
||||
a_.OpMul(temp1_dest, target_src, temp1_src);
|
||||
a_.OpRoundZ(temp1_dest, temp1_src);
|
||||
a_.OpAdd(target_dest, target_src, temp1_src);
|
||||
a.OpMul(temp1_dest, target_src, temp1_src);
|
||||
a.OpRoundZ(temp1_dest, temp1_src);
|
||||
a.OpAdd(target_dest, target_src, temp1_src);
|
||||
// linear *= 1/1023
|
||||
a_.OpMul(target_dest, target_src, dxbc::Src::LF(1.0f / 1023.0f));
|
||||
a.OpMul(target_dest, target_src, dxbc::Src::LF(1.0f / 1023.0f));
|
||||
}
|
||||
|
||||
void DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(
|
||||
uint32_t target_temp, uint32_t target_temp_component, uint32_t source_temp,
|
||||
uint32_t source_temp_component, uint32_t temp_or_target,
|
||||
uint32_t temp_or_target_component, uint32_t temp_non_target,
|
||||
uint32_t temp_non_target_component) {
|
||||
dxbc::Assembler& a, uint32_t target_temp, uint32_t target_temp_component,
|
||||
uint32_t source_temp, uint32_t source_temp_component,
|
||||
uint32_t temp_or_target, uint32_t temp_or_target_component,
|
||||
uint32_t temp_non_target, uint32_t temp_non_target_component) {
|
||||
// The source may be the same as the target, but in this case it can't also be
|
||||
// used as a temporary variable.
|
||||
assert_true(target_temp != source_temp ||
|
||||
@@ -317,28 +318,28 @@ void DxbcShaderTranslator::PreSaturatedLinearToPWLGamma(
|
||||
|
||||
// Get the scale (into temp_or_target) and the offset (into temp_non_target)
|
||||
// for the piece.
|
||||
a_.OpGE(temp_non_target_dest, source_src, dxbc::Src::LF(128.0f / 1023.0f));
|
||||
a_.OpIf(true, temp_non_target_src);
|
||||
a.OpGE(temp_non_target_dest, source_src, dxbc::Src::LF(128.0f / 1023.0f));
|
||||
a.OpIf(true, temp_non_target_src);
|
||||
// [128/1023 ... 1
|
||||
a_.OpGE(temp_non_target_dest, source_src, dxbc::Src::LF(512.0f / 1023.0f));
|
||||
a_.OpMovC(temp_or_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(1023.0f / 8.0f), dxbc::Src::LF(1023.0f / 4.0f));
|
||||
a_.OpMovC(temp_non_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(128.0f / 255.0f), dxbc::Src::LF(64.0f / 255.0f));
|
||||
a_.OpElse();
|
||||
a.OpGE(temp_non_target_dest, source_src, dxbc::Src::LF(512.0f / 1023.0f));
|
||||
a.OpMovC(temp_or_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(1023.0f / 8.0f), dxbc::Src::LF(1023.0f / 4.0f));
|
||||
a.OpMovC(temp_non_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(128.0f / 255.0f), dxbc::Src::LF(64.0f / 255.0f));
|
||||
a.OpElse();
|
||||
// 0 ... 128/1023)
|
||||
a_.OpGE(temp_non_target_dest, source_src, dxbc::Src::LF(64.0f / 1023.0f));
|
||||
a_.OpMovC(temp_or_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(1023.0f / 2.0f), dxbc::Src::LF(1023.0f));
|
||||
a_.OpMovC(temp_non_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(32.0f / 255.0f), dxbc::Src::LF(0.0f));
|
||||
a_.OpEndIf();
|
||||
a.OpGE(temp_non_target_dest, source_src, dxbc::Src::LF(64.0f / 1023.0f));
|
||||
a.OpMovC(temp_or_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(1023.0f / 2.0f), dxbc::Src::LF(1023.0f));
|
||||
a.OpMovC(temp_non_target_dest, temp_non_target_src,
|
||||
dxbc::Src::LF(32.0f / 255.0f), dxbc::Src::LF(0.0f));
|
||||
a.OpEndIf();
|
||||
|
||||
// gamma = trunc(linear * scale) * (1.0 / 255.0) + offset
|
||||
a_.OpMul(target_dest, source_src, temp_or_target_src);
|
||||
a_.OpRoundZ(target_dest, target_src);
|
||||
a_.OpMAd(target_dest, target_src, dxbc::Src::LF(1.0f / 255.0f),
|
||||
temp_non_target_src);
|
||||
a.OpMul(target_dest, source_src, temp_or_target_src);
|
||||
a.OpRoundZ(target_dest, target_src);
|
||||
a.OpMAd(target_dest, target_src, dxbc::Src::LF(1.0f / 255.0f),
|
||||
temp_non_target_src);
|
||||
}
|
||||
|
||||
void DxbcShaderTranslator::RemapAndConvertVertexIndices(
|
||||
@@ -2135,7 +2136,8 @@ constexpr DxbcShaderTranslator::SystemConstantRdef
|
||||
{"xe_texture_swizzled_signs", ShaderRdefTypeIndex::kUint4Array2,
|
||||
sizeof(uint32_t) * 4 * 2},
|
||||
|
||||
{"xe_textures_resolved", ShaderRdefTypeIndex::kUint, sizeof(uint32_t)},
|
||||
{"xe_textures_resolution_scaled", ShaderRdefTypeIndex::kUint,
|
||||
sizeof(uint32_t)},
|
||||
{"xe_sample_count_log2", ShaderRdefTypeIndex::kUint2,
|
||||
sizeof(uint32_t) * 2},
|
||||
{"xe_alpha_test_reference", ShaderRdefTypeIndex::kFloat, sizeof(float)},
|
||||
|
||||
@@ -49,7 +49,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
public:
|
||||
DxbcShaderTranslator(ui::GraphicsProvider::GpuVendorID vendor_id,
|
||||
bool bindless_resources_used, bool edram_rov_used,
|
||||
bool gamma_render_target_as_srgb = false,
|
||||
bool gamma_render_target_as_unorm8 = false,
|
||||
bool msaa_2x_supported = true,
|
||||
uint32_t draw_resolution_scale_x = 1,
|
||||
uint32_t draw_resolution_scale_y = 1,
|
||||
@@ -316,9 +316,8 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// components of each of the 32 used texture fetch constants.
|
||||
uint32_t texture_swizzled_signs[8];
|
||||
|
||||
// Whether the contents of each texture in fetch constants comes from a
|
||||
// resolve operation.
|
||||
uint32_t textures_resolved;
|
||||
// Whether each texture in fetch constants contains resolution-scaled data.
|
||||
uint32_t textures_resolution_scaled;
|
||||
// Log2 of X and Y sample size. Used for alpha to mask, and for MSAA with
|
||||
// ROV, this is used for EDRAM address calculation.
|
||||
uint32_t sample_count_log2[2];
|
||||
@@ -425,7 +424,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
|
||||
kTextureSwizzledSigns,
|
||||
|
||||
kTexturesResolved,
|
||||
kTexturesResolutionScaled,
|
||||
kSampleCountLog2,
|
||||
kAlphaTestReference,
|
||||
|
||||
@@ -578,6 +577,26 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
uint32_t temp2_temp_component,
|
||||
bool remap_to_0_to_0_5);
|
||||
|
||||
// Converts one scalar from piecewise linear gamma to linear. The target may
|
||||
// be the same as the source, the temporary variables must be different. If
|
||||
// the source is not pre-saturated, saturation will be done internally.
|
||||
static void PWLGammaToLinear(dxbc::Assembler& a, uint32_t target_temp,
|
||||
uint32_t target_temp_component,
|
||||
uint32_t source_temp,
|
||||
uint32_t source_temp_component,
|
||||
bool source_pre_saturated, uint32_t temp1,
|
||||
uint32_t temp1_component, uint32_t temp2,
|
||||
uint32_t temp2_component);
|
||||
// Converts one scalar, which must be saturated before calling this function,
|
||||
// from linear to piecewise linear gamma. The target may be the same as either
|
||||
// the source or as temp_or_target, but not as both (and temp_or_target may
|
||||
// not be the same as the source). temp_non_target must be different.
|
||||
static void PreSaturatedLinearToPWLGamma(
|
||||
dxbc::Assembler& a, uint32_t target_temp, uint32_t target_temp_component,
|
||||
uint32_t source_temp, uint32_t source_temp_component,
|
||||
uint32_t temp_or_target, uint32_t temp_or_target_component,
|
||||
uint32_t temp_non_target, uint32_t temp_non_target_component);
|
||||
|
||||
protected:
|
||||
void Reset() override;
|
||||
|
||||
@@ -689,24 +708,6 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// inactive.
|
||||
void ExportToMemory(uint8_t export_eM);
|
||||
|
||||
// Converts one scalar from piecewise linear gamma to linear. The target may
|
||||
// be the same as the source, the temporary variables must be different. If
|
||||
// the source is not pre-saturated, saturation will be done internally.
|
||||
void PWLGammaToLinear(uint32_t target_temp, uint32_t target_temp_component,
|
||||
uint32_t source_temp, uint32_t source_temp_component,
|
||||
bool source_pre_saturated, uint32_t temp1,
|
||||
uint32_t temp1_component, uint32_t temp2,
|
||||
uint32_t temp2_component);
|
||||
// Converts one scalar, which must be saturated before calling this function,
|
||||
// from linear to piecewise linear gamma. The target may be the same as either
|
||||
// the source or as temp_or_target, but not as both (and temp_or_target may
|
||||
// not be the same as the source). temp_non_target must be different.
|
||||
void PreSaturatedLinearToPWLGamma(
|
||||
uint32_t target_temp, uint32_t target_temp_component,
|
||||
uint32_t source_temp, uint32_t source_temp_component,
|
||||
uint32_t temp_or_target, uint32_t temp_or_target_component,
|
||||
uint32_t temp_non_target, uint32_t temp_non_target_component);
|
||||
|
||||
bool IsSampleRate() const {
|
||||
assert_true(is_pixel_shader());
|
||||
return DSV_IsWritingFloat24Depth() && !current_shader().writes_depth();
|
||||
@@ -977,8 +978,9 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
bool edram_rov_used_;
|
||||
|
||||
// Whether with RTV-based output-merger, k_8_8_8_8_GAMMA render targets are
|
||||
// represented as host sRGB.
|
||||
bool gamma_render_target_as_srgb_;
|
||||
// represented as host 8-bit unsigned normalized, and require conversion in
|
||||
// translated shaders.
|
||||
bool gamma_render_target_as_unorm8_;
|
||||
|
||||
// Whether 2x MSAA is emulated using real 2x MSAA rather than two samples of
|
||||
// 4x MSAA.
|
||||
|
||||
@@ -1076,12 +1076,12 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction(
|
||||
if (normalized_components_with_scaled_offsets) {
|
||||
// Using coord_and_sampler_temp.w as a temporary for the needed
|
||||
// resolution scale inverse - sampler not loaded yet.
|
||||
a_.OpAnd(
|
||||
dxbc::Dest::R(coord_and_sampler_temp, 0b1000),
|
||||
LoadSystemConstant(SystemConstants::Index::kTexturesResolved,
|
||||
offsetof(SystemConstants, textures_resolved),
|
||||
dxbc::Src::kXXXX),
|
||||
dxbc::Src::LU(uint32_t(1) << tfetch_index));
|
||||
a_.OpAnd(dxbc::Dest::R(coord_and_sampler_temp, 0b1000),
|
||||
LoadSystemConstant(
|
||||
SystemConstants::Index::kTexturesResolutionScaled,
|
||||
offsetof(SystemConstants, textures_resolution_scaled),
|
||||
dxbc::Src::kXXXX),
|
||||
dxbc::Src::LU(uint32_t(1) << tfetch_index));
|
||||
a_.OpIf(true, dxbc::Src::R(coord_and_sampler_temp, dxbc::Src::kWWWW));
|
||||
a_.OpAdd(
|
||||
dxbc::Dest::R(coord_and_sampler_temp,
|
||||
@@ -1145,12 +1145,12 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction(
|
||||
if (normalized_components_with_scaled_offsets) {
|
||||
// Using coord_and_sampler_temp.w as a temporary for the needed
|
||||
// resolution scale inverse - sampler not loaded yet.
|
||||
a_.OpAnd(
|
||||
dxbc::Dest::R(coord_and_sampler_temp, 0b1000),
|
||||
LoadSystemConstant(SystemConstants::Index::kTexturesResolved,
|
||||
offsetof(SystemConstants, textures_resolved),
|
||||
dxbc::Src::kXXXX),
|
||||
dxbc::Src::LU(uint32_t(1) << tfetch_index));
|
||||
a_.OpAnd(dxbc::Dest::R(coord_and_sampler_temp, 0b1000),
|
||||
LoadSystemConstant(
|
||||
SystemConstants::Index::kTexturesResolutionScaled,
|
||||
offsetof(SystemConstants, textures_resolution_scaled),
|
||||
dxbc::Src::kXXXX),
|
||||
dxbc::Src::LU(uint32_t(1) << tfetch_index));
|
||||
a_.OpIf(true, dxbc::Src::R(coord_and_sampler_temp, dxbc::Src::kWWWW));
|
||||
a_.OpMAd(dxbc::Dest::R(coord_and_sampler_temp,
|
||||
normalized_components_with_scaled_offsets),
|
||||
@@ -2121,56 +2121,9 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction(
|
||||
a_.OpBreak();
|
||||
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::TextureSign::kGamma)));
|
||||
uint32_t gamma_temp = PushSystemTemp();
|
||||
if (gamma_render_target_as_srgb_) {
|
||||
// Check if the texture has sRGB rather that piecewise linear gamma.
|
||||
// More likely that it's just a texture with PWL, put this case in the
|
||||
// `if`, with `else` for sRGB resolved render targets.
|
||||
a_.OpAnd(
|
||||
dxbc::Dest::R(gamma_temp, 0b0001),
|
||||
LoadSystemConstant(SystemConstants::Index::kTexturesResolved,
|
||||
offsetof(SystemConstants, textures_resolved),
|
||||
dxbc::Src::kXXXX),
|
||||
dxbc::Src::LU(uint32_t(1) << tfetch_index));
|
||||
a_.OpIf(false, dxbc::Src::R(gamma_temp, dxbc::Src::kXXXX));
|
||||
}
|
||||
// Convert from piecewise linear.
|
||||
PWLGammaToLinear(system_temp_result_, i, system_temp_result_, i, false,
|
||||
gamma_temp, 0, gamma_temp, 1);
|
||||
if (gamma_render_target_as_srgb_) {
|
||||
a_.OpElse();
|
||||
// Convert from sRGB.
|
||||
a_.OpMov(component_dest, component_src, true);
|
||||
a_.OpGE(dxbc::Dest::R(gamma_temp, 0b0001),
|
||||
dxbc::Src::LF(RenderTargetCache::kSrgbToLinearThreshold),
|
||||
component_src);
|
||||
a_.OpIf(true, dxbc::Src::R(gamma_temp, dxbc::Src::kXXXX));
|
||||
// sRGB <= kSrgbToLinearThreshold case - linear scale.
|
||||
a_.OpMul(component_dest, component_src,
|
||||
dxbc::Src::LF(1.0f /
|
||||
RenderTargetCache::kSrgbToLinearDenominator1));
|
||||
a_.OpElse();
|
||||
// sRGB > kSrgbToLinearThreshold case.
|
||||
// 0 and 1 must be exactly achievable - only convert when the
|
||||
// saturated value is < 1.
|
||||
a_.OpLT(dxbc::Dest::R(gamma_temp, 0b0001), component_src,
|
||||
dxbc::Src::LF(1.0f));
|
||||
a_.OpIf(true, dxbc::Src::R(gamma_temp, dxbc::Src::kXXXX));
|
||||
a_.OpMAd(component_dest, component_src,
|
||||
dxbc::Src::LF(1.0f /
|
||||
RenderTargetCache::kSrgbToLinearDenominator2),
|
||||
dxbc::Src::LF(RenderTargetCache::kSrgbToLinearOffset /
|
||||
RenderTargetCache::kSrgbToLinearDenominator2));
|
||||
a_.OpLog(component_dest, component_src);
|
||||
a_.OpMul(component_dest, component_src,
|
||||
dxbc::Src::LF(RenderTargetCache::kSrgbToLinearExponent));
|
||||
a_.OpExp(component_dest, component_src);
|
||||
// Close the < 1 check.
|
||||
a_.OpEndIf();
|
||||
// Close the sRGB <= kSrgbToLinearThreshold check.
|
||||
a_.OpEndIf();
|
||||
// Close the PWL or sRGB check.
|
||||
a_.OpEndIf();
|
||||
}
|
||||
PWLGammaToLinear(a_, system_temp_result_, i, system_temp_result_, i,
|
||||
false, gamma_temp, 0, gamma_temp, 1);
|
||||
// Release gamma_temp.
|
||||
PopSystemTemp();
|
||||
a_.OpBreak();
|
||||
|
||||
@@ -1197,7 +1197,7 @@ void DxbcShaderTranslator::ROV_UnpackColor(
|
||||
dxbc::Src::LF(1.0f / 255.0f));
|
||||
if (i) {
|
||||
for (uint32_t j = 0; j < 3; ++j) {
|
||||
PWLGammaToLinear(color_temp, j, color_temp, j, true, temp1,
|
||||
PWLGammaToLinear(a_, color_temp, j, color_temp, j, true, temp1,
|
||||
temp1_component, temp2, temp2_component);
|
||||
}
|
||||
}
|
||||
@@ -1350,7 +1350,7 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
|
||||
: xenos::ColorRenderTargetFormat::k_8_8_8_8)));
|
||||
for (uint32_t j = 0; j < 4; ++j) {
|
||||
if (i && j < 3) {
|
||||
PreSaturatedLinearToPWLGamma(temp1, temp1_component, color_temp, j,
|
||||
PreSaturatedLinearToPWLGamma(a_, temp1, temp1_component, color_temp, j,
|
||||
temp1, temp1_component, temp2,
|
||||
temp2_component);
|
||||
// Denormalize and add 0.5 for rounding.
|
||||
@@ -1685,7 +1685,7 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs() {
|
||||
SystemConstants::Index::kColorExpBias,
|
||||
offsetof(SystemConstants, color_exp_bias) + sizeof(float) * i,
|
||||
dxbc::Src::kXXXX));
|
||||
if (!gamma_render_target_as_srgb_) {
|
||||
if (gamma_render_target_as_unorm8_) {
|
||||
// Convert to gamma space - this is incorrect, since it must be done after
|
||||
// blending on the Xbox 360, but this is just one of many blending issues
|
||||
// in the RTV path.
|
||||
@@ -1696,8 +1696,9 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToRTVs() {
|
||||
a_.OpMov(dxbc::Dest::R(system_temp_color, 0b0111),
|
||||
dxbc::Src::R(system_temp_color), true);
|
||||
for (uint32_t j = 0; j < 3; ++j) {
|
||||
PreSaturatedLinearToPWLGamma(system_temp_color, j, system_temp_color, j,
|
||||
gamma_temp, 0, gamma_temp, 1);
|
||||
PreSaturatedLinearToPWLGamma(a_, system_temp_color, j,
|
||||
system_temp_color, j, gamma_temp, 0,
|
||||
gamma_temp, 1);
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
|
||||
@@ -144,16 +144,15 @@ DEFINE_bool(
|
||||
"into account for render-to-texture, for more correct shadow filtering, "
|
||||
"bloom, etc., in some cases.",
|
||||
"GPU");
|
||||
// Disabled by default because of full-screen effects that occur when game
|
||||
// shaders assume piecewise linear (4541080F), much more severe than
|
||||
// blending-related issues.
|
||||
DEFINE_bool(
|
||||
gamma_render_target_as_srgb, false,
|
||||
"When the host can't write piecewise linear gamma directly with correct "
|
||||
"blending, use sRGB output on the host for conceptually correct blending "
|
||||
"in linear color space while having slightly different precision "
|
||||
"distribution in the render target and severely incorrect values if the "
|
||||
"game accesses the resulting colors directly as raw data.",
|
||||
gamma_render_target_as_unorm16, true,
|
||||
"When the host can't write 8 bits per component pixels with piecewise "
|
||||
"linear gamma encoding directly with correct blending, use the 16-bit "
|
||||
"unsigned normalized format, if supported, for conceptually correct "
|
||||
"8_8_8_8_GAMMA render target format blending in linear color space. "
|
||||
"Greatly increases accuracy for this format, but may result in render "
|
||||
"target copying costs if the game switches between 8_8_8_8_GAMMA and "
|
||||
"8_8_8_8 views for the same EDRAM render target.",
|
||||
"GPU");
|
||||
DEFINE_bool(
|
||||
mrt_edram_used_range_clamp_to_min, true,
|
||||
@@ -630,7 +629,6 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
uint32_t edram_bases[1 + xenos::kMaxColorRenderTargets];
|
||||
uint32_t resource_formats[1 + xenos::kMaxColorRenderTargets];
|
||||
uint32_t rts_are_64bpp = 0;
|
||||
uint32_t color_rts_are_gamma = 0;
|
||||
if (is_rasterization_done) {
|
||||
if (normalized_depth_control.z_enable ||
|
||||
normalized_depth_control.stencil_enable) {
|
||||
@@ -659,9 +657,6 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
if (is_64bpp) {
|
||||
rts_are_64bpp |= uint32_t(1) << rt_bit_index;
|
||||
}
|
||||
if (color_format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
color_rts_are_gamma |= uint32_t(1) << i;
|
||||
}
|
||||
xenos::ColorRenderTargetFormat color_resource_format;
|
||||
if (interlock_barrier_only) {
|
||||
// Only changes in mapping between coordinates and addresses are
|
||||
@@ -747,7 +742,6 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
if (!are_accumulated_render_targets_valid_) {
|
||||
std::memset(last_update_accumulated_render_targets_, 0,
|
||||
sizeof(last_update_accumulated_render_targets_));
|
||||
last_update_accumulated_color_targets_are_gamma_ = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -958,26 +952,13 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
std::memcpy(last_update_accumulated_render_targets_,
|
||||
last_update_used_render_targets_,
|
||||
sizeof(last_update_accumulated_render_targets_));
|
||||
last_update_accumulated_color_targets_are_gamma_ = 0;
|
||||
are_accumulated_render_targets_valid_ = true;
|
||||
}
|
||||
// Only update color space of render targets that actually matter here, don't
|
||||
// disable gamma emulation (which may require ending the render pass) on the
|
||||
// host, for example, if making a depth-only draw between color draws with a
|
||||
// gamma target.
|
||||
uint32_t color_rts_used_bits = depth_and_color_rts_used_bits >> 1;
|
||||
// Ignore any render targets dropped before in this function for any reason.
|
||||
color_rts_are_gamma &= color_rts_used_bits;
|
||||
last_update_accumulated_color_targets_are_gamma_ =
|
||||
(last_update_accumulated_color_targets_are_gamma_ &
|
||||
~color_rts_used_bits) |
|
||||
color_rts_are_gamma;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t RenderTargetCache::GetLastUpdateBoundRenderTargets(
|
||||
bool distinguish_gamma_formats,
|
||||
uint32_t* depth_and_color_formats_out) const {
|
||||
if (GetPath() != Path::kHostRenderTargets) {
|
||||
if (depth_and_color_formats_out) {
|
||||
@@ -998,12 +979,7 @@ uint32_t RenderTargetCache::GetLastUpdateBoundRenderTargets(
|
||||
}
|
||||
rts_used |= uint32_t(1) << i;
|
||||
if (depth_and_color_formats_out) {
|
||||
depth_and_color_formats_out[i] =
|
||||
(distinguish_gamma_formats && i &&
|
||||
(last_update_accumulated_color_targets_are_gamma_ &
|
||||
(uint32_t(1) << (i - 1))))
|
||||
? uint32_t(xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA)
|
||||
: render_target->key().resource_format;
|
||||
depth_and_color_formats_out[i] = render_target->key().resource_format;
|
||||
}
|
||||
}
|
||||
return rts_used;
|
||||
|
||||
@@ -32,7 +32,7 @@ DECLARE_bool(depth_transfer_not_equal_test);
|
||||
DECLARE_bool(depth_float24_round);
|
||||
DECLARE_bool(depth_float24_convert_in_pixel_shader);
|
||||
DECLARE_bool(draw_resolution_scaled_texture_offsets);
|
||||
DECLARE_bool(gamma_render_target_as_srgb);
|
||||
DECLARE_bool(gamma_render_target_as_unorm16);
|
||||
DECLARE_bool(native_2x_msaa);
|
||||
DECLARE_bool(native_stencil_value_output);
|
||||
DECLARE_bool(snorm16_render_target_full_range);
|
||||
@@ -72,12 +72,16 @@ class RenderTargetCache {
|
||||
// - 16_16_FLOAT, k_16_16_16_16_FLOAT - the Xenos float16 doesn't have
|
||||
// special values.
|
||||
// Significant differences:
|
||||
// - 8_8_8_8_GAMMA - the piecewise linear gamma curve is very different than
|
||||
// sRGB, one possible path is conversion in shaders (resulting in
|
||||
// incorrect blending, especially visible on decals in 4D5307E6), another
|
||||
// is using sRGB render targets and either conversion on resolve or
|
||||
// reading the resolved data as a true sRGB texture (incorrect when the
|
||||
// game accesses the data directly, like 4541080F).
|
||||
// - 8_8_8_8_GAMMA - the piecewise linear gamma precision distribution
|
||||
// encoding is very different from sRGB. Linear space blending can be
|
||||
// obtained by promoting to R16G16B16A16_UNORM, but for compact storage,
|
||||
// conversion in pixel shader output may be done, though it results in
|
||||
// incorrect blending, especially visible on decals in 4D5307E6. Emulating
|
||||
// by replacing the encoding with sRGB for render target writes and
|
||||
// resolved texture reads could work for some games, but certain games,
|
||||
// such as 4541080F, perform piecewise gamma encoding calculations in
|
||||
// their code, and that produces noticeably incorrect results if the
|
||||
// encoding is changed in guest texture memory.
|
||||
// - 2_10_10_10_FLOAT - ranges significantly different than in float16, much
|
||||
// smaller RGB range, and alpha is fixed-point and has only 2 bits.
|
||||
// - 16_16, 16_16_16_16 - has -32 to 32 range, not -1 to 1 - need either to
|
||||
@@ -91,28 +95,6 @@ class RenderTargetCache {
|
||||
kPixelShaderInterlock,
|
||||
};
|
||||
|
||||
// Useful host-specific values.
|
||||
// sRGB conversion from the Direct3D 11.3 functional specification.
|
||||
static constexpr float kSrgbToLinearDenominator1 = 12.92f;
|
||||
static constexpr float kSrgbToLinearDenominator2 = 1.055f;
|
||||
static constexpr float kSrgbToLinearExponent = 2.4f;
|
||||
static constexpr float kSrgbToLinearOffset = 0.055f;
|
||||
static constexpr float kSrgbToLinearThreshold = 0.04045f;
|
||||
static constexpr float SrgbToLinear(float srgb) {
|
||||
// 0 and 1 must be exactly achievable, also convert NaN to 0.
|
||||
if (!(srgb > 0.0f)) {
|
||||
return 0.0f;
|
||||
}
|
||||
if (!(srgb < 1.0f)) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (srgb <= kSrgbToLinearThreshold) {
|
||||
return srgb / kSrgbToLinearDenominator1;
|
||||
}
|
||||
return std::pow((srgb + kSrgbToLinearOffset) / kSrgbToLinearDenominator2,
|
||||
kSrgbToLinearExponent);
|
||||
}
|
||||
|
||||
// Pixel shader interlock implementation helpers.
|
||||
|
||||
// Appended to the format in the format constant via bitwise OR.
|
||||
@@ -221,7 +203,6 @@ class RenderTargetCache {
|
||||
// formats (resource formats, but if needed, with gamma taken into account) of
|
||||
// each.
|
||||
uint32_t GetLastUpdateBoundRenderTargets(
|
||||
bool distinguish_gamma_formats,
|
||||
uint32_t* depth_and_color_formats_out = nullptr) const;
|
||||
|
||||
protected:
|
||||
@@ -238,6 +219,8 @@ class RenderTargetCache {
|
||||
|
||||
const RegisterFile& register_file() const { return register_file_; }
|
||||
|
||||
virtual bool IsGammaFormatHostStorageSeparate() const = 0;
|
||||
|
||||
// Call last in implementation-specific initialization (when things like path
|
||||
// are initialized by the implementation).
|
||||
void InitializeCommon();
|
||||
@@ -274,7 +257,7 @@ class RenderTargetCache {
|
||||
uint32_t pitch_tiles_at_32bpp : 8; // 19
|
||||
xenos::MsaaSamples msaa_samples : xenos::kMsaaSamplesBits; // 21
|
||||
uint32_t is_depth : 1; // 22
|
||||
// Ignoring the blending precision and sRGB.
|
||||
// Ignoring the blending precision.
|
||||
uint32_t resource_format : xenos::kRenderTargetFormatBits; // 26
|
||||
};
|
||||
|
||||
@@ -548,10 +531,6 @@ class RenderTargetCache {
|
||||
assert_true(GetPath() == Path::kHostRenderTargets);
|
||||
return last_update_accumulated_render_targets_;
|
||||
}
|
||||
uint32_t last_update_accumulated_color_targets_are_gamma() const {
|
||||
assert_true(GetPath() == Path::kHostRenderTargets);
|
||||
return last_update_accumulated_color_targets_are_gamma_;
|
||||
}
|
||||
|
||||
const std::vector<Transfer>* last_update_transfers() const {
|
||||
assert_true(GetPath() == Path::kHostRenderTargets);
|
||||
@@ -697,11 +676,10 @@ class RenderTargetCache {
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr xenos::ColorRenderTargetFormat GetColorResourceFormat(
|
||||
xenos::ColorRenderTargetFormat format) {
|
||||
// sRGB, if used on the host, is a view property or global state - linear
|
||||
// and sRGB host render targets can share data directly without transfers.
|
||||
if (format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
xenos::ColorRenderTargetFormat GetColorResourceFormat(
|
||||
xenos::ColorRenderTargetFormat format) const {
|
||||
if (format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA &&
|
||||
!IsGammaFormatHostStorageSeparate()) {
|
||||
return xenos::ColorRenderTargetFormat::k_8_8_8_8;
|
||||
}
|
||||
return xenos::GetStorageColorFormat(format);
|
||||
@@ -755,10 +733,6 @@ class RenderTargetCache {
|
||||
RenderTarget*
|
||||
last_update_accumulated_render_targets_[1 +
|
||||
xenos::kMaxColorRenderTargets];
|
||||
// Whether the color render targets (in bits 0...3) from the last successful
|
||||
// update have k_8_8_8_8_GAMMA format, for sRGB emulation on the host if
|
||||
// needed.
|
||||
uint32_t last_update_accumulated_color_targets_are_gamma_;
|
||||
// If false, the next update must copy last_update_used_render_targets_ to
|
||||
// last_update_accumulated_render_targets_ - it's not beneficial or even
|
||||
// incorrect to keep the previously bound render targets.
|
||||
|
||||
@@ -161,7 +161,7 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) {
|
||||
#ifndef XE_RESOLVE_CLEAR
|
||||
uint XeResolveDestPixelAddress(const XeResolveInfo resolve_info,
|
||||
uint2_xe host_position,
|
||||
const uint bytes_per_element_log2) {
|
||||
const uint bytes_per_block_log2) {
|
||||
host_position += resolve_info.dest_xy_offset_scaled;
|
||||
uint address;
|
||||
uint2_xe guest_position = host_position;
|
||||
@@ -170,19 +170,18 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) {
|
||||
resolution_scaled_addressing =
|
||||
XeniaTextureGetResolutionScaledAddressing(
|
||||
host_position.xy, resolve_info.resolution_scale,
|
||||
bytes_per_element_log2);
|
||||
bytes_per_block_log2);
|
||||
guest_position = resolution_scaled_addressing.guest_group_origin;
|
||||
#endif
|
||||
dont_flatten_xe if (resolve_info.dest_is_array) {
|
||||
address = uint(XenosTextureTiledAddress3D(
|
||||
int3_xe(uint3_xe(guest_position, resolve_info.dest_slice)),
|
||||
resolve_info.dest_row_pitch_macro_tiles,
|
||||
resolve_info.dest_slice_pitch_3d_macro_tiles,
|
||||
bytes_per_element_log2));
|
||||
resolve_info.dest_slice_pitch_3d_macro_tiles, bytes_per_block_log2));
|
||||
} else {
|
||||
address = uint(XenosTextureTiledAddress2D(
|
||||
int2_xe(guest_position), resolve_info.dest_row_pitch_macro_tiles,
|
||||
bytes_per_element_log2));
|
||||
bytes_per_block_log2));
|
||||
}
|
||||
#ifdef XE_RESOLVE_RESOLUTION_SCALED
|
||||
address = address * (resolve_info.resolution_scale.x *
|
||||
@@ -195,18 +194,18 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) {
|
||||
|
||||
// XOR to apply to the byte address to flip the bits corresponding to the
|
||||
// given X coordinate bits within the macro tile width, or with resolution
|
||||
// scaling, within XeniaTextureResolutionScaledGroupElements.x.
|
||||
// scaling, within XeniaTextureResolutionScaledGroupBlocks.x.
|
||||
// Addition is recommended instead of XOR if the bits are known to be 0 in the
|
||||
// original address, so the host GPU driver can optimize it into a constant
|
||||
// store offset if available in the host hardware shader instruction set
|
||||
// architecture.
|
||||
uint XeResolveLocalXAddressXor(const uint x,
|
||||
const uint bytes_per_element_log2) {
|
||||
const uint bytes_per_block_log2) {
|
||||
#ifdef XE_RESOLVE_RESOLUTION_SCALED
|
||||
return x << bytes_per_element_log2;
|
||||
return x << bytes_per_block_log2;
|
||||
#else
|
||||
return uint(XenosTextureTiledAddressXInMacroXor(int(x),
|
||||
bytes_per_element_log2));
|
||||
bytes_per_block_log2));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,15 @@ int XenosTextureTiledAddressCombine(const int outer_inner_bytes, const int bank,
|
||||
}
|
||||
|
||||
int XenosTextureTiledAddress2D(const int2_xe p, const uint pitch_macro_tiles,
|
||||
const uint bytes_per_element_log2) {
|
||||
const int outer_elements =
|
||||
const uint bytes_per_block_log2) {
|
||||
const int outer_blocks =
|
||||
((p.y >> XENOS_TEXTURE_MACRO_TILE_HEIGHT_2D_LOG2) *
|
||||
int(pitch_macro_tiles) +
|
||||
(p.x >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2))
|
||||
<< 6;
|
||||
const int inner_elements = (((p.y >> 1) & 0x7) << 3) | (p.x & 0x7);
|
||||
const int outer_inner_bytes =
|
||||
(outer_elements | inner_elements) << bytes_per_element_log2;
|
||||
const int inner_blocks = (((p.y >> 1) & 0x7) << 3) | (p.x & 0x7);
|
||||
const int outer_inner_bytes = (outer_blocks | inner_blocks)
|
||||
<< bytes_per_block_log2;
|
||||
const int bank = (p.y >> 4) & 0x1;
|
||||
const int pipe = ((p.x >> 3) & 0x3) ^ (((p.y >> 3) & 0x1) << 1);
|
||||
return XenosTextureTiledAddressCombine(outer_inner_bytes, bank, pipe,
|
||||
@@ -43,18 +43,18 @@ int XenosTextureTiledAddress2D(const int2_xe p, const uint pitch_macro_tiles,
|
||||
|
||||
int XenosTextureTiledAddress3D(const int3_xe p, const uint pitch_macro_tiles,
|
||||
const uint height_macro_tiles,
|
||||
const uint bytes_per_element_log2) {
|
||||
const int outer_elements =
|
||||
const uint bytes_per_block_log2) {
|
||||
const int outer_blocks =
|
||||
((((p.z >> XENOS_TEXTURE_MACRO_TILE_DEPTH_LOG2) *
|
||||
int(height_macro_tiles) +
|
||||
(p.y >> XENOS_TEXTURE_MACRO_TILE_HEIGHT_3D_LOG2)) *
|
||||
int(pitch_macro_tiles)) +
|
||||
(p.x >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2))
|
||||
<< 7;
|
||||
const int inner_elements =
|
||||
const int inner_blocks =
|
||||
((p.z & 0x3) << 5) | (((p.y >> 1) & 0x3) << 3) | (p.x & 0x7);
|
||||
const int outer_inner_bytes =
|
||||
(outer_elements | inner_elements) << bytes_per_element_log2;
|
||||
const int outer_inner_bytes = (outer_blocks | inner_blocks)
|
||||
<< bytes_per_block_log2;
|
||||
const int bank = ((p.y >> 3) ^ (p.z >> 2)) & 0x1;
|
||||
const int pipe = ((p.x >> 3) & 0x3) ^ (bank << 1);
|
||||
return XenosTextureTiledAddressCombine(outer_inner_bytes, bank, pipe,
|
||||
@@ -65,49 +65,49 @@ int XenosTextureTiledAddress3D(const int3_xe p, const uint pitch_macro_tiles,
|
||||
// X coordinate bits within the width of a macro tile.
|
||||
// Note that in a tiled address, bit 7 is X[4] ^ Y[3] ^ Z[2], not X[4] alone.
|
||||
int XenosTextureTiledAddressXInMacroXor(const int x,
|
||||
const uint bytes_per_element_log2) {
|
||||
return XenosTextureTiledAddressCombine((x & 0x7) << bytes_per_element_log2,
|
||||
const uint bytes_per_block_log2) {
|
||||
return XenosTextureTiledAddressCombine((x & 0x7) << bytes_per_block_log2,
|
||||
0, (x >> 3) & 0x3, 0);
|
||||
}
|
||||
|
||||
// The lowest bits of an element index within a micro tile are X[2:0].
|
||||
// The lowest bits of an block index within a micro tile are X[2:0].
|
||||
// In a tiled address, the bit 4 is always Y[0].
|
||||
// However, the bits [3:0] are the lower bits of the micro tile element index
|
||||
// times the number of bytes per element.
|
||||
// Because of this, a number of elements, that depends on the count of bytes per
|
||||
// element, along the X axis (aligned to this amount) is stored consecutively in
|
||||
// However, the bits [3:0] are the lower bits of the micro tile block index
|
||||
// times the number of bytes per block.
|
||||
// Because of this, a number of blocks, that depends on the count of bytes per
|
||||
// block, along the X axis (aligned to this amount) is stored consecutively in
|
||||
// guest memory:
|
||||
// - 1bpe: 8 elements (8 bytes - limited by address bit 3 being Y[1] for 1bpe).
|
||||
// - 2bpe: 8 elements (16 bytes - limited by address bit 4 always being Y[0]).
|
||||
// - 4bpe: 4 elements.
|
||||
// - 8bpe: 2 elements.
|
||||
// - 16bpe: 1 element.
|
||||
// This makes it possible to access multiple elements in a single row using
|
||||
// 8-byte or (for >= 2bpe) 16-byte loads and stores, and that's particularly
|
||||
// useful when transferring texture data between tiled and linear storage.
|
||||
// - 1bpb: 8 blocks (8 bytes - limited by address bit 3 being Y[1] for 1bpb).
|
||||
// - 2bpb: 8 blocks (16 bytes - limited by address bit 4 always being Y[0]).
|
||||
// - 4bpb: 4 blocks.
|
||||
// - 8bpb: 2 blocks.
|
||||
// - 16bpb: 1 block.
|
||||
// This makes it possible to access multiple blocks in a single row using 8-byte
|
||||
// or (for >= 2bpb) 16-byte loads and stores, and that's particularly useful
|
||||
// when transferring texture data between tiled and linear storage.
|
||||
|
||||
// With resolution scaling, one scaled group of bytes in guest addresses
|
||||
// corresponds to `scale.x * scale.y` groups of the same size on the host.
|
||||
//
|
||||
// A single group contains a full rectangular region of elements. This means
|
||||
// that, for instance, if the Y[1] tiled address bit is within the group size,
|
||||
// Y[0] must be within it too, so division is enough to go from host to guest
|
||||
// A single group contains a full rectangular region of blocks. This means that,
|
||||
// for instance, if the Y[1] tiled address bit is within the group size, Y[0]
|
||||
// must be within it too, so division is enough to go from host to guest
|
||||
// coordinates for the origin of the group.
|
||||
//
|
||||
// The address of the guest group on the host is the guest tiled address of its
|
||||
// origin in guest coordinates multiplied by `scale.x * scale.y`.
|
||||
//
|
||||
// Within a guest group, the addressing of elements is controlled by the host.
|
||||
// Specifically, host groups are arranged in a guest group as block-linear
|
||||
// column-major (for storage locality along both axes), and elements in a host
|
||||
// Within a guest group, the addressing of blocks is controlled by the host.
|
||||
// Specifically, host groups are arranged in a guest group as group-linear
|
||||
// column-major (for storage locality along both axes), and blocks in a host
|
||||
// group are laid out as linear row-major (guest tiling therefore is applied
|
||||
// only to whole guest groups, not within them, for simplicity).
|
||||
//
|
||||
// Addressing with resolution scaling is not intended to allow for
|
||||
// reinterpretation of resolution-scaled data between different numbers of bits
|
||||
// per element. Rather, it's designed for simple and efficient access on the
|
||||
// host, primarily when copying between tiled and linear storage, and to reduce
|
||||
// the differences in shader logic between unscaled and scaled data.
|
||||
// reinterpretation of resolution-scaled data between different numbers of bytes
|
||||
// per block. Rather, it's designed for simple and efficient access on the host,
|
||||
// primarily when copying between tiled and linear storage, and to reduce the
|
||||
// differences in shader logic between unscaled and scaled data.
|
||||
//
|
||||
// However, the groups are still small enough to preserve most of the tiling
|
||||
// properties on a macro level, most importantly the possibility to resolve
|
||||
@@ -120,33 +120,33 @@ int XenosTextureTiledAddressXInMacroXor(const int x,
|
||||
// resolution-scaled data in a memory range to be done with the number of bytes
|
||||
// per pixel being the only needed metadata.
|
||||
//
|
||||
// A common pattern in Xenia is copying multiple 8-byte or (for >= 2bpe) 16-byte
|
||||
// sequences of consecutive elements along the X axis in a single shader
|
||||
// A common pattern in Xenia is copying multiple 8-byte or (for >= 2bpb) 16-byte
|
||||
// sequences of consecutive blocks along the X axis in a single shader
|
||||
// invocation, by computing the tiled address once and merely flipping X bits in
|
||||
// it.
|
||||
//
|
||||
// With the resolution scaling group size being no larger than 2^7 bytes, it may
|
||||
// contain guest X bits [3:0] for <= 4bpe, [1:0] for 8bpe, and [0] for 16bpe
|
||||
// (note that though X[3] always goes to address[6], for 8bpe, X[2] is
|
||||
// address[8], so a group can't be wider than 4 elements, and similarly for X[1]
|
||||
// for 16bpe).
|
||||
// contain guest X bits [3:0] for <= 4bpb, [1:0] for 8bpb, and [0] for 16bpb
|
||||
// (note that though X[3] always goes to address[6], for 8bpb, X[2] is
|
||||
// address[8], so a group can't be wider than 4 blocks, and similarly for X[1]
|
||||
// for 16bpb).
|
||||
//
|
||||
// Given these requirements, the group sizes are chosen as follows:
|
||||
// - 1bpe - lower 7 bits of an unscaled address are X0, X1, X2, Y1, Y0, Y2, X3:
|
||||
// - Group width: 2^4 elements (maximum within 7 bits), or 2^4 bytes.
|
||||
// - Group height: 2^3 elements (Y[2:0] between X[3:0]).
|
||||
// - 2bpe - lower 7 bits of an unscaled address are 0, X0, X1, X2, Y0, Y1, X3:
|
||||
// - Group width: 2^4 elements (maximum within 7 bits), or 2^5 bytes.
|
||||
// - Group height: 2^2 elements (Y[1:0] between X[3:0]).
|
||||
// - 4bpe - lower 7 bits of an unscaled address are 0, 0, X0, X1, Y0, X2, X3:
|
||||
// - Group width: 2^4 elements (maximum within 7 bits), or 2^6 bytes.
|
||||
// - Group height: 2^1 elements (Y[0] between X[3:0]).
|
||||
// - 8bpe - lower 7 bits of an unscaled address are 0, 0, 0, X0, Y0, X1, X3:
|
||||
// - Group width: 2^2 elements (X[2] is beyond 7 bits), or 2^5 bytes.
|
||||
// - Group height: 2^1 elements (Y[0] between X[1:0]).
|
||||
// - 16bpe - lower 7 bits of an unscaled address are 0, 0, 0, 0, Y0, X0, X3:
|
||||
// - Group width: 2^1 elements (X[2:1] is beyond 7 bits), or 2^5 bytes.
|
||||
// - Group height: 2^1 elements (Y[0] below X[0]).
|
||||
// - 1bpb - lower 7 bits of an unscaled address are X0, X1, X2, Y1, Y0, Y2, X3:
|
||||
// - Group width: 2^4 blocks (maximum within 7 bits), or 2^4 bytes.
|
||||
// - Group height: 2^3 blocks (Y[2:0] between X[3:0]).
|
||||
// - 2bpb - lower 7 bits of an unscaled address are 0, X0, X1, X2, Y0, Y1, X3:
|
||||
// - Group width: 2^4 blocks (maximum within 7 bits), or 2^5 bytes.
|
||||
// - Group height: 2^2 blocks (Y[1:0] between X[3:0]).
|
||||
// - 4bpb - lower 7 bits of an unscaled address are 0, 0, X0, X1, Y0, X2, X3:
|
||||
// - Group width: 2^4 blocks (maximum within 7 bits), or 2^6 bytes.
|
||||
// - Group height: 2^1 blocks (Y[0] between X[3:0]).
|
||||
// - 8bpb - lower 7 bits of an unscaled address are 0, 0, 0, X0, Y0, X1, X3:
|
||||
// - Group width: 2^2 blocks (X[2] is beyond 7 bits), or 2^5 bytes.
|
||||
// - Group height: 2^1 blocks (Y[0] between X[1:0]).
|
||||
// - 16bpb - lower 7 bits of an unscaled address are 0, 0, 0, 0, Y0, X0, X3:
|
||||
// - Group width: 2^1 blocks (X[2:1] is beyond 7 bits), or 2^5 bytes.
|
||||
// - Group height: 2^1 blocks (Y[0] below X[0]).
|
||||
//
|
||||
// 2^6 bytes copied per invocation is likely to be optimal, as that consumes 16
|
||||
// 32-bit VGPRs, out of a total of 24 (1024 / 40 rounded down to 4) available
|
||||
@@ -154,19 +154,17 @@ int XenosTextureTiledAddressXInMacroXor(const int x,
|
||||
// (although the occupancy of copy shaders is likely to be limited by memory
|
||||
// accesses instead anyway).
|
||||
//
|
||||
// Note that with the given group sizes, as well as with elements in a host
|
||||
// group stored as row-major, for 1bpe, 16x1 host elements are stored
|
||||
// consecutively with resolution scaling (even though in guest tiling, only 8x1
|
||||
// elements are), so they can be accessed via one 16-byte operation rather than
|
||||
// two 8-byte ones.
|
||||
// Note that with the given group sizes, as well as with blocks in a host group
|
||||
// stored as row-major, for 1bpb, 16x1 host blocks are stored consecutively with
|
||||
// resolution scaling (even though in guest tiling, only 8x1 blocks are), so
|
||||
// they can be accessed via one 16-byte operation rather than two 8-byte ones.
|
||||
|
||||
// Expected to be called for a compile-time constant.
|
||||
uint2_xe XeniaTextureResolutionScaledGroupElementsLog2(
|
||||
const uint bytes_per_element_log2) {
|
||||
uint2_xe XeniaTextureResolutionScaledGroupBlocksLog2(
|
||||
const uint bytes_per_block_log2) {
|
||||
// Based on the tiled address properties, see the comment above for details.
|
||||
return uint2_xe(
|
||||
bytes_per_element_log2 >= 3u ? 5u - bytes_per_element_log2 : 4u,
|
||||
3u - min(bytes_per_element_log2, 2u));
|
||||
return uint2_xe(bytes_per_block_log2 >= 3u ? 5u - bytes_per_block_log2 : 4u,
|
||||
3u - min(bytes_per_block_log2, 2u));
|
||||
}
|
||||
|
||||
struct XeniaTextureResolutionScaledAddressing {
|
||||
@@ -177,20 +175,20 @@ struct XeniaTextureResolutionScaledAddressing {
|
||||
XeniaTextureResolutionScaledAddressing
|
||||
XeniaTextureGetResolutionScaledAddressing(const uint2_xe position,
|
||||
const uint2_xe resolution_scale,
|
||||
const uint bytes_per_element_log2) {
|
||||
const uint bytes_per_block_log2) {
|
||||
XeniaTextureResolutionScaledAddressing addressing;
|
||||
|
||||
const uint2_xe group_elements_log2 =
|
||||
XeniaTextureResolutionScaledGroupElementsLog2(bytes_per_element_log2);
|
||||
const uint2_xe group_blocks_log2 =
|
||||
XeniaTextureResolutionScaledGroupBlocksLog2(bytes_per_block_log2);
|
||||
|
||||
const uint2_xe host_group_id_in_texture = position >> group_elements_log2;
|
||||
const uint2_xe host_group_id_in_texture = position >> group_blocks_log2;
|
||||
const uint2_xe guest_group_id_in_texture =
|
||||
host_group_id_in_texture / resolution_scale;
|
||||
const uint2_xe host_group_id_in_guest_group =
|
||||
host_group_id_in_texture - resolution_scale * guest_group_id_in_texture;
|
||||
|
||||
addressing.guest_group_origin =
|
||||
guest_group_id_in_texture << group_elements_log2;
|
||||
guest_group_id_in_texture << group_blocks_log2;
|
||||
|
||||
// Host groups are stored as column-major in a guest group, but this can be
|
||||
// changed freely.
|
||||
@@ -198,18 +196,18 @@ XeniaTextureGetResolutionScaledAddressing(const uint2_xe position,
|
||||
host_group_id_in_guest_group.x * resolution_scale.y +
|
||||
host_group_id_in_guest_group.y;
|
||||
// Shifts are expanded rather than chained because the number of bytes per
|
||||
// element, and thus also the group size, are expected to be compile-time
|
||||
// block, and thus also the group size, are expected to be compile-time
|
||||
// constants, so this is expected to be combined using GPU bitfield insert
|
||||
// instructions.
|
||||
const uint group_width_bytes_log2 =
|
||||
group_elements_log2.x + bytes_per_element_log2;
|
||||
group_blocks_log2.x + bytes_per_block_log2;
|
||||
const uint2_xe position_in_host_group =
|
||||
position & ((uint_x2_xe(1u) << group_elements_log2) - 1u);
|
||||
position & ((uint_x2_xe(1u) << group_blocks_log2) - 1u);
|
||||
addressing.host_byte_offset_in_guest_group =
|
||||
(host_group_index_in_guest_group <<
|
||||
(group_width_bytes_log2 + group_elements_log2.y)) |
|
||||
(group_width_bytes_log2 + group_blocks_log2.y)) |
|
||||
(position_in_host_group.y << group_width_bytes_log2) |
|
||||
(position_in_host_group.x << bytes_per_element_log2);
|
||||
(position_in_host_group.x << bytes_per_block_log2);
|
||||
|
||||
return addressing;
|
||||
}
|
||||
|
||||
@@ -98,14 +98,14 @@ XeTextureLoadInfo XeTextureLoadGetInfo(param_push_consts_xe) {
|
||||
|
||||
uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info,
|
||||
const uint3_xe host_position,
|
||||
const uint bytes_per_element_log2) {
|
||||
const uint bytes_per_block_log2) {
|
||||
uint address;
|
||||
uint3_xe guest_position = host_position;
|
||||
#ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED
|
||||
const XeniaTextureResolutionScaledAddressing resolution_scaled_addressing =
|
||||
XeniaTextureGetResolutionScaledAddressing(host_position.xy,
|
||||
load_info.resolution_scale,
|
||||
bytes_per_element_log2);
|
||||
bytes_per_block_log2);
|
||||
guest_position.xy = resolution_scaled_addressing.guest_group_origin;
|
||||
#else
|
||||
dont_flatten_xe if (!load_info.is_tiled) {
|
||||
@@ -114,7 +114,7 @@ uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info,
|
||||
(guest_position.y +
|
||||
load_info.guest_z_stride_block_rows_aligned *
|
||||
guest_position.z)) <<
|
||||
bytes_per_element_log2;
|
||||
bytes_per_block_log2;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -124,12 +124,12 @@ uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info,
|
||||
load_info.guest_pitch_aligned >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2,
|
||||
load_info.guest_z_stride_block_rows_aligned >>
|
||||
XENOS_TEXTURE_MACRO_TILE_HEIGHT_3D_LOG2,
|
||||
bytes_per_element_log2));
|
||||
bytes_per_block_log2));
|
||||
} else {
|
||||
address = uint(XenosTextureTiledAddress2D(
|
||||
int2_xe(guest_position.xy),
|
||||
load_info.guest_pitch_aligned >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2,
|
||||
bytes_per_element_log2));
|
||||
bytes_per_block_log2));
|
||||
}
|
||||
}
|
||||
#ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED
|
||||
@@ -143,24 +143,24 @@ uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info,
|
||||
|
||||
// XOR to apply to the byte address to flip the bits corresponding to the given
|
||||
// X coordinate bits within:
|
||||
// - Resolution-scaled tiled: XeniaTextureResolutionScaledGroupElements.x;
|
||||
// - Resolution-scaled tiled: XeniaTextureResolutionScaledGroupBlocks.x;
|
||||
// - Unscaled tiled: macro tile width;
|
||||
// - Linear: 256 bytes.
|
||||
// Addition is recommended instead of XOR if the bits are known to be 0 in the
|
||||
// original address, so the host GPU driver can optimize it into a constant load
|
||||
// offset if available in the host hardware shader instruction set architecture.
|
||||
uint XeTextureLoadLocalXAddressXor(const uint x,
|
||||
const uint bytes_per_element_log2,
|
||||
const uint bytes_per_block_log2,
|
||||
const bool is_tiled) {
|
||||
uint x_address_xor;
|
||||
#ifndef XE_TEXTURE_LOAD_RESOLUTION_SCALED
|
||||
dont_flatten_xe if (is_tiled) {
|
||||
x_address_xor = uint(
|
||||
XenosTextureTiledAddressXInMacroXor(int(x), bytes_per_element_log2));
|
||||
XenosTextureTiledAddressXInMacroXor(int(x), bytes_per_block_log2));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
x_address_xor = x << bytes_per_element_log2;
|
||||
x_address_xor = x << bytes_per_block_log2;
|
||||
}
|
||||
return x_address_xor;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ cbuffer xe_system_cbuffer : register(b0) {
|
||||
|
||||
uint4 xe_texture_swizzled_signs[2];
|
||||
|
||||
uint xe_textures_resolved;
|
||||
uint xe_textures_resolution_scaled;
|
||||
uint2 xe_sample_count_log2;
|
||||
float xe_alpha_test_reference;
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ bool SharedMemory::InitializeCommon() {
|
||||
((kBufferSize >> page_size_log2_) + 63) / 64;
|
||||
num_system_page_flags_ = static_cast<uint32_t>(num_system_page_flags_entries);
|
||||
|
||||
// Allocate double-buffered valid flags (2x) plus gpu_resolved and gpu_written
|
||||
// (1x each) = 4 total arrays. With 2048 entries per array and 8 bytes per
|
||||
// entry, that's 65536 bytes total (adds 16KB for double buffering).
|
||||
// Allocate double-buffered valid flags (2x) plus gpu_written (1x) = 3 total
|
||||
// arrays. With 2048 entries per array and 8 bytes per entry, that's 49152
|
||||
// bytes total.
|
||||
|
||||
uint64_t* system_page_flags_base = (uint64_t*)memory::AllocFixed(
|
||||
nullptr, num_system_page_flags_ * 4 * sizeof(uint64_t),
|
||||
nullptr, num_system_page_flags_ * 3 * sizeof(uint64_t),
|
||||
memory::AllocationType::kReserveCommit, memory::PageAccess::kReadWrite);
|
||||
|
||||
if (!system_page_flags_base) {
|
||||
@@ -44,16 +44,12 @@ bool SharedMemory::InitializeCommon() {
|
||||
// Set up double buffer for valid flags
|
||||
valid_buffer_a_ = system_page_flags_base;
|
||||
valid_buffer_b_ = system_page_flags_base + num_system_page_flags_;
|
||||
system_page_flags_valid_and_gpu_resolved_ =
|
||||
system_page_flags_base + (num_system_page_flags_ * 2);
|
||||
system_page_flags_valid_and_gpu_written_ =
|
||||
system_page_flags_base + (num_system_page_flags_ * 3);
|
||||
system_page_flags_base + (num_system_page_flags_ * 2);
|
||||
|
||||
// Initialize both valid buffers to zero
|
||||
// Initialize all buffers to zero
|
||||
memset(valid_buffer_a_, 0, 8 * num_system_page_flags_entries);
|
||||
memset(valid_buffer_b_, 0, 8 * num_system_page_flags_entries);
|
||||
memset(system_page_flags_valid_and_gpu_resolved_, 0,
|
||||
8 * num_system_page_flags_entries);
|
||||
memset(system_page_flags_valid_and_gpu_written_, 0,
|
||||
8 * num_system_page_flags_entries);
|
||||
|
||||
@@ -125,7 +121,6 @@ void SharedMemory::ShutdownCommon() {
|
||||
staging_valid_flags_.store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
system_page_flags_valid_and_gpu_resolved_ = nullptr;
|
||||
system_page_flags_valid_and_gpu_written_ = nullptr;
|
||||
num_system_page_flags_ = 0;
|
||||
}
|
||||
@@ -141,8 +136,6 @@ void SharedMemory::InvalidateAllPages() {
|
||||
std::memset(staging, 0, num_system_page_flags_ * sizeof(uint64_t));
|
||||
std::memset(system_page_flags_valid_and_gpu_written_, 0,
|
||||
num_system_page_flags_ * sizeof(uint64_t));
|
||||
std::memset(system_page_flags_valid_and_gpu_resolved_, 0,
|
||||
num_system_page_flags_ * sizeof(uint64_t));
|
||||
|
||||
// Mark all blocks as dirty and set dirty flag
|
||||
dirty_blocks_.store(0xFFFFFFFF, std::memory_order_relaxed);
|
||||
@@ -370,8 +363,7 @@ void SharedMemory::FireWatches(uint32_t page_first, uint32_t page_last,
|
||||
}
|
||||
}
|
||||
|
||||
void SharedMemory::RangeWrittenByGpu(uint32_t start, uint32_t length,
|
||||
bool is_resolve) {
|
||||
void SharedMemory::RangeWrittenByGpu(uint32_t start, uint32_t length) {
|
||||
if (length == 0 || start >= kBufferSize) {
|
||||
return;
|
||||
}
|
||||
@@ -386,7 +378,7 @@ void SharedMemory::RangeWrittenByGpu(uint32_t start, uint32_t length,
|
||||
|
||||
// Mark the range as valid (so pages are not reuploaded until modified by the
|
||||
// CPU) and watch it so the CPU can reuse it and this will be caught.
|
||||
MakeRangeValid(start, length, true, is_resolve);
|
||||
MakeRangeValid(start, length, true);
|
||||
}
|
||||
|
||||
bool SharedMemory::AllocateSparseHostGpuMemoryRange(
|
||||
@@ -398,9 +390,7 @@ bool SharedMemory::AllocateSparseHostGpuMemoryRange(
|
||||
}
|
||||
|
||||
void SharedMemory::MakeRangeValid(uint32_t start, uint32_t length,
|
||||
bool written_by_gpu,
|
||||
bool written_by_gpu_resolve) {
|
||||
assert_false(written_by_gpu_resolve && !written_by_gpu);
|
||||
bool written_by_gpu) {
|
||||
if (length == 0 || start >= kBufferSize) {
|
||||
return;
|
||||
}
|
||||
@@ -438,11 +428,6 @@ void SharedMemory::MakeRangeValid(uint32_t start, uint32_t length,
|
||||
} else {
|
||||
system_page_flags_valid_and_gpu_written_[i] &= ~valid_bits;
|
||||
}
|
||||
if (written_by_gpu_resolve) {
|
||||
system_page_flags_valid_and_gpu_resolved_[i] |= valid_bits;
|
||||
} else {
|
||||
system_page_flags_valid_and_gpu_resolved_[i] &= ~valid_bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,14 +461,11 @@ void SharedMemory::UnlinkWatchRange(WatchRange* range) {
|
||||
range->next_free = watch_range_first_free_;
|
||||
watch_range_first_free_ = range;
|
||||
}
|
||||
|
||||
// todo: optimize, an enormous amount of cpu time (1.34%) is spent here.
|
||||
bool SharedMemory::RequestRange(uint32_t start, uint32_t length,
|
||||
bool* any_data_resolved_out) {
|
||||
bool SharedMemory::RequestRange(uint32_t start, uint32_t length) {
|
||||
if (!length) {
|
||||
// Some texture or buffer is empty, for example - safe to draw in this case.
|
||||
if (any_data_resolved_out) {
|
||||
*any_data_resolved_out = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (start > kBufferSize || (kBufferSize - start) < length) {
|
||||
@@ -526,9 +508,6 @@ bool SharedMemory::RequestRange(uint32_t start, uint32_t length,
|
||||
}
|
||||
if (all_valid) {
|
||||
// All pages already valid, nothing to upload
|
||||
if (any_data_resolved_out) {
|
||||
*any_data_resolved_out = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -538,23 +517,18 @@ bool SharedMemory::RequestRange(uint32_t start, uint32_t length,
|
||||
std::pair<uint32_t, uint32_t>* uploads =
|
||||
reinterpret_cast<std::pair<uint32_t, uint32_t>*>(upload_ranges_.data());
|
||||
|
||||
bool any_data_resolved = false;
|
||||
// swcache::PrefetchL1(&system_page_flags_[block_first]);
|
||||
uint32_t range_start = UINT32_MAX;
|
||||
|
||||
{
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
TryFindUploadRange(block_first, block_last, page_first, page_last,
|
||||
any_data_resolved, range_start, current_upload_range,
|
||||
uploads);
|
||||
range_start, current_upload_range, uploads);
|
||||
}
|
||||
if (range_start != UINT32_MAX) {
|
||||
uploads[current_upload_range++] =
|
||||
(std::make_pair(range_start, page_last + 1 - range_start));
|
||||
}
|
||||
if (any_data_resolved_out) {
|
||||
*any_data_resolved_out = any_data_resolved;
|
||||
}
|
||||
if (!current_upload_range) {
|
||||
return true;
|
||||
}
|
||||
@@ -575,7 +549,6 @@ void SharedMemory::TryFindUploadRange(const uint32_t& block_first,
|
||||
const uint32_t& block_last,
|
||||
const uint32_t& page_first,
|
||||
const uint32_t& page_last,
|
||||
bool& any_data_resolved,
|
||||
uint32_t& range_start,
|
||||
unsigned int& current_upload_range,
|
||||
std::pair<uint32_t, uint32_t>* uploads) {
|
||||
@@ -584,29 +557,14 @@ void SharedMemory::TryFindUploadRange(const uint32_t& block_first,
|
||||
uint64_t* valid_flags = active_valid_flags_.load(std::memory_order_relaxed);
|
||||
|
||||
for (uint32_t i = block_first; i <= block_last; ++i) {
|
||||
// const SystemPageFlagsBlock& block = system_page_flags_[i];
|
||||
uint64_t block_valid = valid_flags[i];
|
||||
uint64_t block_resolved = 0;
|
||||
|
||||
if (any_data_resolved) {
|
||||
block_resolved = 0;
|
||||
} else {
|
||||
block_resolved = system_page_flags_valid_and_gpu_resolved_[i];
|
||||
}
|
||||
if (i == block_first) {
|
||||
uint64_t block_before = mod_shift_left(uint64_t(1), page_first) - 1;
|
||||
block_valid |= block_before;
|
||||
block_resolved &= ~block_before;
|
||||
}
|
||||
if (i == block_last && (page_last & 63) != 63) {
|
||||
uint64_t block_inside = mod_shift_left(uint64_t(1), page_last + 1) - 1;
|
||||
block_valid |= ~block_inside;
|
||||
block_resolved &= block_inside;
|
||||
}
|
||||
// Consider pages in the block outside the requested range valid.
|
||||
if (!block_resolved) {
|
||||
} else {
|
||||
any_data_resolved = true;
|
||||
}
|
||||
TryGetNextUploadRange(range_start, block_valid, i, current_upload_range,
|
||||
uploads);
|
||||
@@ -728,7 +686,6 @@ std::pair<uint32_t, uint32_t> SharedMemory::MemoryInvalidationCallback(
|
||||
}
|
||||
uint64_t* valid_flags = active_valid_flags_.load(std::memory_order_relaxed);
|
||||
valid_flags[i] &= ~invalidate_bits;
|
||||
system_page_flags_valid_and_gpu_resolved_[i] &= ~invalidate_bits;
|
||||
system_page_flags_valid_and_gpu_written_[i] &= ~invalidate_bits;
|
||||
// Track which 64-entry blocks are dirty for partial copying.
|
||||
dirty_blocks_mask |= (1u << (i >> 6));
|
||||
|
||||
@@ -75,13 +75,12 @@ class SharedMemory {
|
||||
// Checks if the range has been updated, uploads new data if needed and
|
||||
// ensures the host GPU memory backing the range are resident. Returns true if
|
||||
// the range has been fully updated and is usable.
|
||||
bool RequestRange(uint32_t start, uint32_t length,
|
||||
bool* any_data_resolved_out = nullptr);
|
||||
bool RequestRange(uint32_t start, uint32_t length);
|
||||
|
||||
void TryFindUploadRange(const uint32_t& block_first,
|
||||
const uint32_t& block_last,
|
||||
const uint32_t& page_first, const uint32_t& page_last,
|
||||
bool& any_data_resolved, uint32_t& range_start,
|
||||
uint32_t& range_start,
|
||||
unsigned int& current_upload_range,
|
||||
std::pair<uint32_t, uint32_t>* uploads);
|
||||
|
||||
@@ -104,7 +103,7 @@ class SharedMemory {
|
||||
// be called, to make sure, if the GPU writes don't overwrite *everything* in
|
||||
// the pages they touch, the CPU data is properly loaded to the unmodified
|
||||
// regions in those pages.
|
||||
void RangeWrittenByGpu(uint32_t start, uint32_t length, bool is_resolve);
|
||||
void RangeWrittenByGpu(uint32_t start, uint32_t length);
|
||||
|
||||
protected:
|
||||
SharedMemory(Memory& memory);
|
||||
@@ -138,8 +137,7 @@ class SharedMemory {
|
||||
uint32_t length_allocations);
|
||||
|
||||
// Mark the memory range as updated and protect it.
|
||||
void MakeRangeValid(uint32_t start, uint32_t length, bool written_by_gpu,
|
||||
bool written_by_gpu_resolve);
|
||||
void MakeRangeValid(uint32_t start, uint32_t length, bool written_by_gpu);
|
||||
|
||||
// Uploads a range of host pages - only called if host GPU sparse memory
|
||||
// allocation succeeded if needed. While uploading, MakeRangeValid must be
|
||||
@@ -208,9 +206,6 @@ class SharedMemory {
|
||||
// Subset of valid pages - whether each page in the GPU buffer contains data
|
||||
// that was written on the GPU, thus should not be invalidated spuriously.
|
||||
uint64_t valid_and_gpu_written;
|
||||
// Subset of valid_and_gpu_written - whether each page in the GPU buffer
|
||||
// contains data written specifically by resolving from EDRAM.
|
||||
uint64_t valid_and_gpu_resolved;
|
||||
};
|
||||
|
||||
// chrispy: todo, systempageflagsblock should be 3 different arrays
|
||||
@@ -236,8 +231,7 @@ class SharedMemory {
|
||||
// this reduces copy overhead by 80-95%.
|
||||
std::atomic<uint32_t> dirty_blocks_{0};
|
||||
|
||||
uint64_t *system_page_flags_valid_and_gpu_written_ = nullptr,
|
||||
*system_page_flags_valid_and_gpu_resolved_ = nullptr;
|
||||
uint64_t* system_page_flags_valid_and_gpu_written_ = nullptr;
|
||||
unsigned num_system_page_flags_ = 0;
|
||||
static std::pair<uint32_t, uint32_t> MemoryInvalidationCallbackThunk(
|
||||
void* context_ptr, uint32_t physical_address_start, uint32_t length,
|
||||
|
||||
@@ -397,7 +397,6 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
SpirvShaderTranslator(
|
||||
const Features& features, bool native_2x_msaa_with_attachments,
|
||||
bool native_2x_msaa_no_attachments, bool edram_fragment_shader_interlock,
|
||||
bool gamma_render_target_as_srgb = false,
|
||||
uint32_t draw_resolution_scale_x = 1,
|
||||
uint32_t draw_resolution_scale_y = 1,
|
||||
ui::vulkan::SpirvToolsContext* spirv_tools_context = nullptr,
|
||||
@@ -406,7 +405,6 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
native_2x_msaa_with_attachments_(native_2x_msaa_with_attachments),
|
||||
native_2x_msaa_no_attachments_(native_2x_msaa_no_attachments),
|
||||
edram_fragment_shader_interlock_(edram_fragment_shader_interlock),
|
||||
gamma_render_target_as_srgb_(gamma_render_target_as_srgb),
|
||||
draw_resolution_scale_x_(draw_resolution_scale_x),
|
||||
draw_resolution_scale_y_(draw_resolution_scale_y),
|
||||
spirv_tools_context_(spirv_tools_context),
|
||||
@@ -796,9 +794,6 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
// flow of the main function, and that there are no returns before either
|
||||
// (there's a single return from the shader).
|
||||
bool edram_fragment_shader_interlock_;
|
||||
// Whether with host render targets, k_8_8_8_8_GAMMA render targets are
|
||||
// represented as host sRGB (gamma applied by the host after blending).
|
||||
bool gamma_render_target_as_srgb_;
|
||||
|
||||
// Is currently writing the empty depth-only pixel shader, such as for depth
|
||||
// and stencil testing with fragment shader interlock.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user