Merge remote-tracking branch 'xenia-master/master' into edge

This commit is contained in:
Herman S.
2026-01-20 11:39:24 +09:00
34 changed files with 566 additions and 717 deletions
+3
View File
@@ -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
+9 -13
View File
@@ -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.
+122 -57
View File
@@ -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
+2 -3
View File
@@ -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);
+6 -3
View File
@@ -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(),
+2 -2
View File
@@ -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);
+51 -49
View File
@@ -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)},
+27 -25
View File
@@ -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.
+14 -61
View File
@@ -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();
+6 -5
View File
@@ -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();
}
+9 -33
View File
@@ -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;
+18 -44
View File
@@ -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.
+8 -9
View File
@@ -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
}
+74 -76
View File
@@ -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;
}
+9 -9
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+12 -55
View File
@@ -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));
+5 -11
View File
@@ -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,
-5
View File
@@ -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