Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental

This commit is contained in:
Gliniak
2022-12-31 10:53:25 +01:00
38 changed files with 632 additions and 6570 deletions

View File

@@ -403,12 +403,17 @@ void EmulatorWindow::DisplayConfigDialog::OnDraw(ImGuiIO& io) {
new_presenter_config.GetFsrSharpnessReduction();
ImGui::TextUnformatted(
"FSR sharpness reduction when upscaling (lower is sharper):");
// Power 2.0 as the reduction is in stops, used in exp2.
const auto label =
fmt::format("{:.3f} stops", fsr_sharpness_reduction);
// Power 2.0 scaling as the reduction is in stops, used in exp2.
fsr_sharpness_reduction = sqrt(2.f * fsr_sharpness_reduction);
ImGui::SliderFloat(
"##FSRSharpnessReduction", &fsr_sharpness_reduction,
ui::Presenter::GuestOutputPaintConfig::kFsrSharpnessReductionMin,
ui::Presenter::GuestOutputPaintConfig::kFsrSharpnessReductionMax,
"%.3f stops", 2.0f);
label.c_str(), ImGuiSliderFlags_NoInput);
fsr_sharpness_reduction =
.5f * fsr_sharpness_reduction * fsr_sharpness_reduction;
ImGui::SameLine();
if (ImGui::Button("Reset##ResetFSRSharpnessReduction")) {
fsr_sharpness_reduction = ui::Presenter::GuestOutputPaintConfig ::

View File

@@ -28,6 +28,7 @@ DEFINE_int64(x64_extension_mask, -1LL,
" 512 = AVX512VL\n"
" 1024 = AVX512BW\n"
" 2048 = AVX512DQ\n"
" 4096 = AVX512VBMI\n"
" -1 = Detect and utilize all possible processor features\n",
"x64");
namespace xe {

View File

@@ -28,13 +28,14 @@ enum X64FeatureFlags : uint64_t {
kX64EmitAVX512BW = 1 << 10,
kX64EmitAVX512DQ = 1 << 11,
kX64EmitAVX512VBMI = 1 << 12,
kX64EmitAVX512Ortho = kX64EmitAVX512F | kX64EmitAVX512VL,
kX64EmitAVX512Ortho64 = kX64EmitAVX512Ortho | kX64EmitAVX512DQ,
kX64FastJrcx = 1 << 12, // jrcxz is as fast as any other jump ( >= Zen1)
kX64FastJrcx = 1 << 13, // jrcxz is as fast as any other jump ( >= Zen1)
kX64FastLoop =
1 << 13, // loop/loope/loopne is as fast as any other jump ( >= Zen2)
kX64EmitAVX512VBMI = 1 << 14,
1 << 14, // loop/loope/loopne is as fast as any other jump ( >= Zen2)
kX64FlagsIndependentVars =
1 << 15, // if true, instructions that only modify some flags (like
// inc/dec) do not introduce false dependencies on EFLAGS
@@ -54,7 +55,7 @@ uint64_t GetFeatureFlags();
XE_COLD
void InitFeatureFlags();
}
} // namespace amd64
} // namespace xe
#endif // XENIA_BASE_PLATFORM_AMD64_H_

View File

@@ -755,6 +755,19 @@ struct VECTOR_SUB
// src1/src2.
e.vpsubd(e.xmm1, src1, src2);
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) {
// If the result is less or equal to the first operand then
// we did not underflow
Opmask not_underflow = e.k1;
// _mm_cmple_epu32_mask
e.vpcmpud(not_underflow, e.xmm1, src1, 0x2);
// Copy over values that did not underflow, write zero
// everywhere else
e.vmovdqa32(dest | not_underflow | e.T_z, e.xmm1);
return;
}
// If result is greater than either of the inputs, we've
// underflowed (only need to check one input)
// if (res > src1) then underflowed
@@ -766,6 +779,21 @@ struct VECTOR_SUB
} else {
e.vpsubd(e.xmm1, src1, src2);
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho |
kX64EmitAVX512DQ)) {
e.vmovdqa32(e.xmm3, src1);
e.vpternlogd(e.xmm3, e.xmm1, src2, 0b00011000);
const Opmask saturate = e.k1;
e.vpmovd2m(saturate, e.xmm3);
e.vpsrad(e.xmm2, e.xmm1, 31);
e.vpxord(e.xmm2, e.xmm2, e.GetXmmConstPtr(XMMSignMaskI32));
e.vpblendmd(dest | saturate, e.xmm1, e.xmm2);
return;
}
// We can only overflow if the signs of the operands are
// opposite. If signs are opposite and result sign isn't the
// same as src1's sign, we've overflowed. if ((s32b)((src1 ^
@@ -2296,7 +2324,23 @@ struct PERMUTE_V128
} else {
e.vxorps(e.xmm0, i.src1, e.GetXmmConstPtr(XMMSwapWordMask));
}
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho | kX64EmitAVX512VBMI)) {
Xmm table_lo = e.xmm1;
if (i.src2.is_constant) {
e.LoadConstantXmm(table_lo, i.src2.constant());
} else {
table_lo = i.src2;
}
Opmask zeroes = e.k1;
// _mm_cmple_epu8_mask
e.vpcmpub(zeroes, e.xmm0, e.GetXmmConstPtr(XMMPermuteControl15), 2);
e.vpermb(i.dest.reg() | zeroes | e.T_z, e.xmm0, table_lo);
return;
}
e.vpand(e.xmm0, e.GetXmmConstPtr(XMMPermuteByteMask));
if (i.src2.is_constant) {
e.LoadConstantXmm(i.dest, i.src2.constant());
e.vpshufb(i.dest, i.dest, e.xmm0);
@@ -2312,6 +2356,39 @@ struct PERMUTE_V128
// General permute.
// Control mask needs to be shuffled.
// TODO(benvanik): do constants here instead of in generated code.
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho | kX64EmitAVX512BW |
kX64EmitAVX512VBMI)) {
Xmm table_idx = e.xmm0;
if (i.src1.is_constant) {
e.LoadConstantXmm(table_idx, i.src1.constant());
e.vxorps(table_idx, table_idx, e.GetXmmConstPtr(XMMSwapWordMask));
} else {
e.vxorps(table_idx, i.src1, e.GetXmmConstPtr(XMMSwapWordMask));
}
Xmm table_lo = e.xmm1;
if (i.src2.value->IsConstantZero()) {
e.vpxor(table_lo, table_lo);
} else if (i.src2.is_constant) {
e.LoadConstantXmm(table_lo, i.src2.constant());
} else {
table_lo = i.src2;
}
Xmm table_hi = e.xmm2;
if (i.src3.value->IsConstantZero()) {
e.vpxor(table_hi, table_hi);
} else if (i.src3.is_constant) {
e.LoadConstantXmm(table_hi, i.src3.constant());
} else {
table_hi = i.src3;
}
e.vpermi2b(table_idx, table_lo, table_hi);
e.vmovdqu8(i.dest, table_idx);
return;
}
if (i.src1.is_constant) {
e.LoadConstantXmm(e.xmm2, i.src1.constant());
e.vxorps(e.xmm2, e.xmm2, e.GetXmmConstPtr(XMMSwapWordMask));
@@ -2319,6 +2396,7 @@ struct PERMUTE_V128
e.vxorps(e.xmm2, i.src1, e.GetXmmConstPtr(XMMSwapWordMask));
}
e.vpand(e.xmm2, e.GetXmmConstPtr(XMMPermuteByteMask));
Xmm src2_shuf = e.xmm0;
if (i.src2.value->IsConstantZero()) {
e.vpxor(src2_shuf, src2_shuf);
@@ -2346,8 +2424,39 @@ struct PERMUTE_V128
static void EmitByInt16(X64Emitter& e, const EmitArgType& i) {
// src1 is an array of indices corresponding to positions within src2 and
// src3.
// src3
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho | kX64EmitAVX512BW)) {
e.LoadConstantXmm(e.xmm1, vec128s(0x1));
Xmm table_idx = e.xmm0;
if (i.src1.is_constant) {
e.LoadConstantXmm(table_idx, i.src1.constant());
e.vpxord(table_idx, table_idx, e.xmm1);
} else {
e.vpxord(table_idx, i.src1, e.xmm1);
}
Xmm table_lo = e.xmm1;
if (i.src2.is_constant) {
e.LoadConstantXmm(table_lo, i.src2.constant());
} else {
table_lo = i.src2;
}
Xmm table_hi = e.xmm2;
if (i.src3.is_constant) {
e.LoadConstantXmm(table_hi, i.src3.constant());
} else {
table_hi = i.src3;
}
e.vpermi2w(table_idx, table_lo, table_hi);
e.vmovdqu8(i.dest, table_idx);
return;
}
assert_true(i.src1.is_constant);
vec128_t perm = (i.src1.constant() & vec128s(0xF)) ^ vec128s(0x1);
vec128_t perm_ctrl = vec128b(0);
for (int i = 0; i < 8; i++) {

View File

@@ -37,6 +37,7 @@
#include "xenia/cpu/backend/x64/x64_tracers.h"
// needed for stmxcsr
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
#include "xenia/cpu/backend/x64/x64_util.h"
#include "xenia/cpu/hir/hir_builder.h"
#include "xenia/cpu/processor.h"
@@ -700,6 +701,26 @@ struct SELECT_F64
static void Emit(X64Emitter& e, const EmitArgType& i) {
e.ChangeMxcsrMode(MXCSRMode::Fpu);
// dest = src1 != 0 ? src2 : src3
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) {
e.movzx(e.rax, i.src1);
e.vmovq(e.xmm0, e.rax);
e.vptestmq(e.k1, e.xmm0, e.xmm0);
const Xmm src2 = i.src2.is_constant ? e.xmm1 : i.src2;
if (i.src2.is_constant) {
e.LoadConstantXmm(src2, i.src2.constant());
}
const Xmm src3 = i.src3.is_constant ? e.xmm2 : i.src3;
if (i.src3.is_constant) {
e.LoadConstantXmm(src3, i.src3.constant());
}
e.vpblendmq(i.dest.reg() | e.k1, src3, src2);
return;
}
e.movzx(e.eax, i.src1);
e.vmovd(e.xmm1, e.eax);
e.vpxor(e.xmm0, e.xmm0);
@@ -762,7 +783,7 @@ struct SELECT_V128_V128
: Sequence<SELECT_V128_V128,
I<OPCODE_SELECT, V128Op, V128Op, V128Op, V128Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
Xmm src1 = i.src1.is_constant ? e.xmm0 : i.src1;
const Xmm src1 = i.src1.is_constant ? e.xmm0 : i.src1;
PermittedBlend mayblend = GetPermittedBlendForSelectV128(i.src1.value);
// todo: detect whether src1 is only 0 or FFFF and use blends if so.
// currently we only detect cmps
@@ -770,16 +791,25 @@ struct SELECT_V128_V128
e.LoadConstantXmm(src1, i.src1.constant());
}
Xmm src2 = i.src2.is_constant ? e.xmm1 : i.src2;
const Xmm src2 = i.src2.is_constant ? e.xmm1 : i.src2;
if (i.src2.is_constant) {
e.LoadConstantXmm(src2, i.src2.constant());
}
Xmm src3 = i.src3.is_constant ? e.xmm2 : i.src3;
const Xmm src3 = i.src3.is_constant ? e.xmm2 : i.src3;
if (i.src3.is_constant) {
e.LoadConstantXmm(src3, i.src3.constant());
}
if (e.IsFeatureEnabled(kX64EmitAVX512Ortho)) {
e.vmovdqa(e.xmm3, src1);
e.vpternlogd(e.xmm3, src2, src3,
(~TernaryOperand::a & TernaryOperand::b) |
(TernaryOperand::c & TernaryOperand::a));
e.vmovdqa(i.dest, e.xmm3);
return;
}
if (mayblend == PermittedBlend::Int8) {
e.vpblendvb(i.dest, src2, src3, src1);
} else if (mayblend == PermittedBlend::Ps) {

View File

@@ -0,0 +1,46 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_BACKEND_X64_X64_UTIL_H_
#define XENIA_CPU_BACKEND_X64_X64_UTIL_H_
#include "xenia/base/vec128.h"
#include "xenia/cpu/backend/x64/x64_backend.h"
#include "xenia/cpu/backend/x64/x64_emitter.h"
namespace xe {
namespace cpu {
namespace backend {
namespace x64 {
// Used to generate ternary logic truth-tables for vpternlog
// Use these to directly refer to terms and perform binary operations upon them
// and the resulting value will be the ternary lookup table
// ex:
// (TernaryOperand::a | ~TernaryOperand::b) & TernaryOperand::c
// = 0b10100010
// = 0xa2
// vpternlog a, b, c, 0xa2
//
// ~(TernaryOperand::a ^ TernaryOperand::b) & TernaryOperand::c
// = 0b10000010
// = 0x82
// vpternlog a, b, c, 0x82
namespace TernaryOperand {
constexpr uint8_t a = 0b11110000;
constexpr uint8_t b = 0b11001100;
constexpr uint8_t c = 0b10101010;
} // namespace TernaryOperand
} // namespace x64
} // namespace backend
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_BACKEND_X64_X64_UTIL_H_

View File

@@ -39,9 +39,6 @@ bool FinalizationPass::Run(HIRBuilder* builder) {
auto block = builder->first_block();
while (block) {
block->ordinal = block_ordinal++;
// Ensure all labels have names.
// Remove unneeded jumps.
auto tail = block->instr_tail;
if (tail && tail->opcode == &OPCODE_BRANCH_info) {

View File

@@ -367,7 +367,7 @@ void DebugWindow::DrawSourcePane() {
ImGui::PushButtonRepeat(true);
bool can_step = !cache_.is_running && state_.thread_info;
if (ImGui::ButtonEx("Step PPC", ImVec2(0, 0),
can_step ? 0 : ImGuiButtonFlags_Disabled)) {
can_step ? 0 : ImGuiItemFlags_Disabled)) {
// By enabling the button when stepping we allow repeat behavior.
if (processor_->execution_state() != cpu::ExecutionState::kStepping) {
processor_->StepGuestInstruction(state_.thread_info->thread_id);
@@ -385,7 +385,7 @@ void DebugWindow::DrawSourcePane() {
ImGui::SameLine();
ImGui::PushButtonRepeat(true);
if (ImGui::ButtonEx("Step x64", ImVec2(0, 0),
can_step ? 0 : ImGuiButtonFlags_Disabled)) {
can_step ? 0 : ImGuiItemFlags_Disabled)) {
// By enabling the button when stepping we allow repeat behavior.
if (processor_->execution_state() != cpu::ExecutionState::kStepping) {
processor_->StepHostInstruction(state_.thread_info->thread_id);

View File

@@ -876,15 +876,17 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
// Show achievments data
XELOGI("-------------------- ACHIEVEMENTS --------------------");
auto ach = db.GetAchievements();
for (const auto& entry : ach) {
const std::vector<kernel::util::XdbfAchievementTableEntry>
achievement_list = db.GetAchievements();
for (const kernel::util::XdbfAchievementTableEntry& entry :
achievement_list) {
std::string label = db.GetStringTableEntry(language, entry.label_id);
std::string desc =
db.GetStringTableEntry(language, entry.description_id);
XELOGI("{} - {} - {} - {}", entry.id, label, desc, entry.gamerscore);
}
XELOGI("----------------- END OF ACHIEVEMENTS --------------------");
XELOGI("----------------- END OF ACHIEVEMENTS ----------------");
auto icon_block = db.icon();
if (icon_block) {

View File

@@ -1038,6 +1038,36 @@ bool D3D12CommandProcessor::SetupContext() {
parameter.Descriptor.RegisterSpace = 0;
parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
}
// Shared memory SRV and UAV.
D3D12_DESCRIPTOR_RANGE root_shared_memory_view_ranges[2];
{
auto& parameter =
root_parameters_bindless[kRootParameter_Bindless_SharedMemory];
parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameter.DescriptorTable.NumDescriptorRanges =
uint32_t(xe::countof(root_shared_memory_view_ranges));
parameter.DescriptorTable.pDescriptorRanges =
root_shared_memory_view_ranges;
parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
{
auto& range = root_shared_memory_view_ranges[0];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
range.NumDescriptors = 1;
range.BaseShaderRegister =
UINT(DxbcShaderTranslator::SRVMainRegister::kSharedMemory);
range.RegisterSpace = UINT(DxbcShaderTranslator::SRVSpace::kMain);
range.OffsetInDescriptorsFromTableStart = 0;
}
{
auto& range = root_shared_memory_view_ranges[1];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
range.NumDescriptors = 1;
range.BaseShaderRegister =
UINT(DxbcShaderTranslator::UAVRegister::kSharedMemory);
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = 1;
}
}
// Sampler heap.
D3D12_DESCRIPTOR_RANGE root_bindless_sampler_range;
{
@@ -1057,7 +1087,7 @@ bool D3D12CommandProcessor::SetupContext() {
root_bindless_sampler_range.OffsetInDescriptorsFromTableStart = 0;
}
// View heap.
D3D12_DESCRIPTOR_RANGE root_bindless_view_ranges[6];
D3D12_DESCRIPTOR_RANGE root_bindless_view_ranges[4];
{
auto& parameter =
root_parameters_bindless[kRootParameter_Bindless_ViewHeap];
@@ -1066,34 +1096,6 @@ bool D3D12CommandProcessor::SetupContext() {
parameter.DescriptorTable.NumDescriptorRanges = 0;
parameter.DescriptorTable.pDescriptorRanges = root_bindless_view_ranges;
parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
// Shared memory SRV.
{
assert_true(parameter.DescriptorTable.NumDescriptorRanges <
xe::countof(root_bindless_view_ranges));
auto& range = root_bindless_view_ranges[parameter.DescriptorTable
.NumDescriptorRanges++];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
range.NumDescriptors = 1;
range.BaseShaderRegister =
UINT(DxbcShaderTranslator::SRVMainRegister::kSharedMemory);
range.RegisterSpace = UINT(DxbcShaderTranslator::SRVSpace::kMain);
range.OffsetInDescriptorsFromTableStart =
UINT(SystemBindlessView::kSharedMemoryRawSRV);
}
// Shared memory UAV.
{
assert_true(parameter.DescriptorTable.NumDescriptorRanges <
xe::countof(root_bindless_view_ranges));
auto& range = root_bindless_view_ranges[parameter.DescriptorTable
.NumDescriptorRanges++];
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
range.NumDescriptors = 1;
range.BaseShaderRegister =
UINT(DxbcShaderTranslator::UAVRegister::kSharedMemory);
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart =
UINT(SystemBindlessView::kSharedMemoryRawUAV);
}
// EDRAM.
if (render_target_cache_->GetPath() ==
RenderTargetCache::Path::kPixelShaderInterlock) {
@@ -1458,6 +1460,20 @@ bool D3D12CommandProcessor::SetupContext() {
if (bindless_resources_used_) {
// Create the system bindless descriptors once all resources are
// initialized.
// kNullRawSRV.
ui::d3d12::util::CreateBufferRawSRV(
device,
provider.OffsetViewDescriptor(
view_bindless_heap_cpu_start_,
uint32_t(SystemBindlessView::kNullRawSRV)),
nullptr, 0);
// kNullRawUAV.
ui::d3d12::util::CreateBufferRawUAV(
device,
provider.OffsetViewDescriptor(
view_bindless_heap_cpu_start_,
uint32_t(SystemBindlessView::kNullRawUAV)),
nullptr, 0);
// kNullTexture2DArray.
D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc;
null_srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
@@ -2731,7 +2747,8 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
used_texture_mask, normalized_depth_control, normalized_color_mask);
// Update constant buffers, descriptors and root parameters.
if (!UpdateBindings(vertex_shader, pixel_shader, root_signature)) {
if (!UpdateBindings(vertex_shader, pixel_shader, root_signature,
memexport_used)) {
return false;
}
// Must not call anything that can change the descriptor heap from now on!
@@ -3413,6 +3430,7 @@ bool D3D12CommandProcessor::BeginSubmission(bool is_guest_command) {
cbuffer_binding_float_pixel_.up_to_date = false;
cbuffer_binding_bool_loop_.up_to_date = false;
cbuffer_binding_fetch_.up_to_date = false;
current_shared_memory_binding_is_uav_.reset();
if (bindless_resources_used_) {
cbuffer_binding_descriptor_indices_vertex_.up_to_date = false;
cbuffer_binding_descriptor_indices_pixel_.up_to_date = false;
@@ -4299,9 +4317,10 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
}
}
bool D3D12CommandProcessor::UpdateBindings(
const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader,
ID3D12RootSignature* root_signature) {
bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
const D3D12Shader* pixel_shader,
ID3D12RootSignature* root_signature,
bool shared_memory_is_uav) {
const ui::d3d12::D3D12Provider& provider = GetD3D12Provider();
ID3D12Device* device = provider.GetDevice();
const RegisterFile& regs = *register_file_;
@@ -4338,6 +4357,9 @@ bool D3D12CommandProcessor::UpdateBindings(
uint32_t root_parameter_bool_loop_constants =
bindless_resources_used_ ? kRootParameter_Bindless_BoolLoopConstants
: kRootParameter_Bindful_BoolLoopConstants;
uint32_t root_parameter_shared_memory_and_bindful_edram =
bindless_resources_used_ ? kRootParameter_Bindless_SharedMemory
: kRootParameter_Bindful_SharedMemoryAndEdram;
//
// Update root constant buffers that are common for bindful and bindless.
@@ -4505,6 +4527,13 @@ bool D3D12CommandProcessor::UpdateBindings(
// Update descriptors.
//
if (!current_shared_memory_binding_is_uav_.has_value() ||
current_shared_memory_binding_is_uav_.value() != shared_memory_is_uav) {
current_shared_memory_binding_is_uav_ = shared_memory_is_uav;
current_graphics_root_up_to_date_ &=
~(1u << root_parameter_shared_memory_and_bindful_edram);
}
// Get textures and samplers used by the vertex shader, check if the last used
// samplers are compatible and update them.
size_t texture_layout_uid_vertex =
@@ -4815,7 +4844,9 @@ bool D3D12CommandProcessor::UpdateBindings(
bool retval = UpdateBindings_BindfulPath(
texture_layout_uid_vertex, textures_vertex, texture_layout_uid_pixel,
textures_pixel, sampler_count_vertex, sampler_count_pixel, retflag);
if (retflag) return retval;
if (retflag) {
return retval;
}
}
// Update the root parameters.
@@ -4854,6 +4885,31 @@ bool D3D12CommandProcessor::UpdateBindings(
current_graphics_root_up_to_date_ |= 1u
<< root_parameter_bool_loop_constants;
}
if (!(current_graphics_root_up_to_date_ &
(1u << root_parameter_shared_memory_and_bindful_edram))) {
assert_true(current_shared_memory_binding_is_uav_.has_value());
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_shared_memory_and_bindful_edram;
if (bindless_resources_used_) {
gpu_handle_shared_memory_and_bindful_edram =
provider.OffsetViewDescriptor(
view_bindless_heap_gpu_start_,
uint32_t(current_shared_memory_binding_is_uav_.value()
? SystemBindlessView ::
kNullRawSRVAndSharedMemoryRawUAVStart
: SystemBindlessView ::
kSharedMemoryRawSRVAndNullRawUAVStart));
} else {
gpu_handle_shared_memory_and_bindful_edram =
current_shared_memory_binding_is_uav_.value()
? gpu_handle_shared_memory_uav_and_edram_
: gpu_handle_shared_memory_srv_and_edram_;
}
deferred_command_list_.D3DSetGraphicsRootDescriptorTable(
root_parameter_shared_memory_and_bindful_edram,
gpu_handle_shared_memory_and_bindful_edram);
current_graphics_root_up_to_date_ |=
1u << root_parameter_shared_memory_and_bindful_edram;
}
if (bindless_resources_used_) {
if (!(current_graphics_root_up_to_date_ &
(1u << kRootParameter_Bindless_DescriptorIndicesPixel))) {
@@ -4895,14 +4951,6 @@ bool D3D12CommandProcessor::UpdateBindings(
XE_COLD
XE_NOINLINE
void D3D12CommandProcessor::UpdateBindings_UpdateRootBindful() {
if (!(current_graphics_root_up_to_date_ &
(1u << kRootParameter_Bindful_SharedMemoryAndEdram))) {
deferred_command_list_.D3DSetGraphicsRootDescriptorTable(
kRootParameter_Bindful_SharedMemoryAndEdram,
gpu_handle_shared_memory_and_edram_);
current_graphics_root_up_to_date_ |=
1u << kRootParameter_Bindful_SharedMemoryAndEdram;
}
uint32_t extra_index;
extra_index = current_graphics_root_bindful_extras_.textures_pixel;
if (extra_index != RootBindfulExtraParameterIndices::kUnavailable &&
@@ -5031,7 +5079,7 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
bindful_textures_written_pixel_ = false;
// If updating fully, write the shared memory SRV and UAV descriptors and,
// if needed, the EDRAM descriptor.
gpu_handle_shared_memory_and_edram_ = view_gpu_handle;
gpu_handle_shared_memory_srv_and_edram_ = view_gpu_handle;
shared_memory_->WriteRawSRVDescriptor(view_cpu_handle);
view_cpu_handle.ptr += descriptor_size_view;
view_gpu_handle.ptr += descriptor_size_view;
@@ -5043,6 +5091,19 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
view_cpu_handle.ptr += descriptor_size_view;
view_gpu_handle.ptr += descriptor_size_view;
}
// Null SRV + UAV + EDRAM.
gpu_handle_shared_memory_uav_and_edram_ = view_gpu_handle;
ui::d3d12::util::CreateBufferRawSRV(provider.GetDevice(), view_cpu_handle, nullptr, 0);
view_cpu_handle.ptr += descriptor_size_view;
view_gpu_handle.ptr += descriptor_size_view;
shared_memory_->WriteRawUAVDescriptor(view_cpu_handle);
view_cpu_handle.ptr += descriptor_size_view;
view_gpu_handle.ptr += descriptor_size_view;
if (edram_rov_used) {
render_target_cache_->WriteEdramUintPow2UAVDescriptor(view_cpu_handle, 2);
view_cpu_handle.ptr += descriptor_size_view;
view_gpu_handle.ptr += descriptor_size_view;
}
current_graphics_root_up_to_date_ &=
~(1u << kRootParameter_Bindful_SharedMemoryAndEdram);
}

View File

@@ -14,6 +14,7 @@
#include <atomic>
#include <deque>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
@@ -136,11 +137,19 @@ class D3D12CommandProcessor final : public CommandProcessor {
uint32_t count, ui::d3d12::util::DescriptorCpuGpuHandlePair* handles_out);
// These are needed often, so they are always allocated.
enum class SystemBindlessView : uint32_t {
kSharedMemoryRawSRV,
// Both may be bound as one root parameter.
kSharedMemoryRawSRVAndNullRawUAVStart,
kSharedMemoryRawSRV = kSharedMemoryRawSRVAndNullRawUAVStart,
kNullRawUAV,
// Both may be bound as one root parameter.
kNullRawSRVAndSharedMemoryRawUAVStart,
kNullRawSRV = kNullRawSRVAndSharedMemoryRawUAVStart,
kSharedMemoryRawUAV,
kSharedMemoryR32UintSRV,
kSharedMemoryR32G32UintSRV,
kSharedMemoryR32G32B32A32UintSRV,
kSharedMemoryRawUAV,
kSharedMemoryR32UintUAV,
kSharedMemoryR32G32UintUAV,
kSharedMemoryR32G32B32A32UintUAV,
@@ -348,10 +357,10 @@ class D3D12CommandProcessor final : public CommandProcessor {
kRootParameter_Bindful_SystemConstants, // +2 = 6 in all.
// Pretty rarely used and rarely changed - flow control constants.
kRootParameter_Bindful_BoolLoopConstants, // +2 = 8 in all.
// Never changed except for when starting a new descriptor heap - shared
// memory byte address buffer, and, if ROV is used for EDRAM, EDRAM R32_UINT
// UAV.
// SRV/UAV descriptor table.
// Changed only when starting a new descriptor heap or when switching
// between shared memory as SRV and UAV - shared memory byte address buffer
// (as SRV and as UAV, either may be null if not used), and, if ROV is used
// for EDRAM, EDRAM R32_UINT UAV.
kRootParameter_Bindful_SharedMemoryAndEdram, // +1 = 9 in all.
kRootParameter_Bindful_Count_Base,
@@ -375,10 +384,14 @@ class D3D12CommandProcessor final : public CommandProcessor {
kRootParameter_Bindless_DescriptorIndicesVertex, // +2 = 6 in VS.
kRootParameter_Bindless_SystemConstants, // +2 = 8 in all.
kRootParameter_Bindless_BoolLoopConstants, // +2 = 10 in all.
// Changed only when switching between shared memory as SRV and UAV - shared
// memory byte address buffer (as SRV and as UAV, either may be null if not
// used).
kRootParameter_Bindless_SharedMemory, // +1 = 11 in all.
// Unbounded sampler descriptor table - changed in case of overflow.
kRootParameter_Bindless_SamplerHeap, // +1 = 11 in all.
kRootParameter_Bindless_SamplerHeap, // +1 = 12 in all.
// Unbounded SRV/UAV descriptor table - never changed.
kRootParameter_Bindless_ViewHeap, // +1 = 12 in all.
kRootParameter_Bindless_ViewHeap, // +1 = 13 in all.
kRootParameter_Bindless_Count,
};
@@ -467,7 +480,8 @@ class D3D12CommandProcessor final : public CommandProcessor {
uint32_t normalized_color_mask);
bool UpdateBindings(const D3D12Shader* vertex_shader,
const D3D12Shader* pixel_shader,
ID3D12RootSignature* root_signature);
ID3D12RootSignature* root_signature,
bool shared_memory_is_uav);
XE_COLD
XE_NOINLINE
void UpdateBindings_UpdateRootBindful();
@@ -739,6 +753,13 @@ class D3D12CommandProcessor final : public CommandProcessor {
ConstantBufferBinding cbuffer_binding_descriptor_indices_vertex_;
ConstantBufferBinding cbuffer_binding_descriptor_indices_pixel_;
// Whether the latest shared memory and EDRAM buffer binding contains the
// shared memory UAV rather than the SRV.
// Separate descriptor tables for the SRV and the UAV, even though only one is
// accessed dynamically in the shaders, are used to prevent a validation
// message about missing resource states in PIX.
std::optional<bool> current_shared_memory_binding_is_uav_;
// Pages with the descriptors currently used for handling Xenos draw calls.
uint64_t draw_view_bindful_heap_index_;
uint64_t draw_sampler_bindful_heap_index_;
@@ -771,7 +792,8 @@ class D3D12CommandProcessor final : public CommandProcessor {
std::vector<uint32_t> current_sampler_bindless_indices_pixel_;
// Latest bindful descriptor handles used for handling Xenos draw calls.
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_shared_memory_and_edram_;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_shared_memory_srv_and_edram_;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_shared_memory_uav_and_edram_;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_textures_vertex_;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_textures_pixel_;
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle_samplers_vertex_;

View File

@@ -1226,15 +1226,13 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
builder_->makeStructType(struct_per_vertex_members, "gl_PerVertex");
builder_->addMemberName(type_struct_per_vertex,
kOutputPerVertexMemberPosition, "gl_Position");
builder_->addMemberDecoration(type_struct_per_vertex,
kOutputPerVertexMemberPosition,
spv::DecorationInvariant);
builder_->addMemberDecoration(type_struct_per_vertex,
kOutputPerVertexMemberPosition,
spv::DecorationBuiltIn, spv::BuiltInPosition);
builder_->addDecoration(type_struct_per_vertex, spv::DecorationBlock);
output_per_vertex_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassOutput, type_struct_per_vertex, "");
builder_->addDecoration(output_per_vertex_, spv::DecorationInvariant);
main_interface_.push_back(output_per_vertex_);
}

View File

@@ -265,10 +265,9 @@ TextureGuestLayout GetGuestTextureLayout(
}
layout.mips_total_extent_bytes = 0;
const FormatInfo* const format_info = FormatInfo::Get(format);
const uint32_t bytes_per_block = format_info->bytes_per_block();
const unsigned char block_width_sh = FormatInfo::GetWidthShift(format);
const unsigned char block_height_sh = FormatInfo::GetHeightShift(format);
const FormatInfo* format_info = FormatInfo::Get(format);
uint32_t bytes_per_block = format_info->bytes_per_block();
// The loop counter can mean two things depending on whether the packed mip
// tail is stored as mip 0, because in this case, it would be ambiguous since
// both the base and the mips would be on "level 0", but stored separately and
@@ -313,13 +312,10 @@ TextureGuestLayout GetGuestTextureLayout(
z_slice_stride_texel_rows_unaligned =
std::max(xe::next_pow2(height_texels) >> level, uint32_t(1));
}
// maybe do 1 << block_width_sh instead of format_info->block_width, since
// we'll have cl loaded with the shift anyway
uint32_t row_pitch_blocks_tile_aligned =
xe::align(xe::align<uint32_t>(row_pitch_texels_unaligned,
format_info->block_width) >>
block_width_sh,
xenos::kTextureTileWidthHeight);
uint32_t row_pitch_blocks_tile_aligned = xe::align(
xe::align(row_pitch_texels_unaligned, format_info->block_width) /
format_info->block_width,
xenos::kTextureTileWidthHeight);
level_layout.row_pitch_bytes =
row_pitch_blocks_tile_aligned * bytes_per_block;
// Assuming the provided pitch is already 256-byte-aligned for linear, but
@@ -331,11 +327,10 @@ TextureGuestLayout GetGuestTextureLayout(
}
level_layout.z_slice_stride_block_rows =
dimension != xenos::DataDimension::k1D
? xe::align<uint32_t>(
xe::align<uint32_t>(z_slice_stride_texel_rows_unaligned,
format_info->block_height) >>
block_height_sh,
xenos::kTextureTileWidthHeight)
? xe::align(xe::align(z_slice_stride_texel_rows_unaligned,
format_info->block_height) /
format_info->block_height,
xenos::kTextureTileWidthHeight)
: 1;
level_layout.array_slice_stride_bytes =
level_layout.row_pitch_bytes * level_layout.z_slice_stride_block_rows;
@@ -352,90 +347,76 @@ TextureGuestLayout GetGuestTextureLayout(
// be smaller (especially in the 1280x720 linear k_8_8_8_8 case in 4E4D083E,
// for which memory exactly for 1280x720 is allocated, and aligning the
// height to 32 would cause access of an unallocated page) or bigger than
// the stride. For tiled textures, this is the dimensions aligned to 32x32x4
// blocks (or x1 for the missing dimensions).
uint32_t level_width_blocks =
xe::align<uint32_t>(std::max(width_texels >> level, uint32_t(1)),
format_info->block_width) >>
block_width_sh;
uint32_t level_height_blocks =
xe::align<uint32_t>(std::max(height_texels >> level, uint32_t(1)),
format_info->block_height) >>
block_height_sh;
uint32_t level_depth = std::max(depth >> level, uint32_t(1));
if (is_tiled) {
// the stride.
if (level == layout.packed_level) {
// Calculate the portion of the mip tail actually used by the needed mips.
// The actually used region may be significantly smaller than the full
// 32x32-texel-aligned (and, for mips, calculated from the base dimensions
// rounded to powers of two - 58410A7A has an 80x260 tiled texture with
// packed mips at level 3 containing a mip ending at Y = 36, while
// 260 >> 3 == 32, but 512 >> 3 == 64) tail. A 2x2 texture (for example,
// in 494707D4, there's a 2x2 k_8_8_8_8 linear texture with packed mips),
// for instance, would have its 2x2 base at (16, 0) and its 1x1 mip at
// (8, 0) - and we need 2 or 1 rows in these cases, not 32 - the 32 rows
// in a linear texture (with 256-byte pitch alignment) would span two 4 KB
// pages rather than one.
level_layout.x_extent_blocks = 0;
level_layout.y_extent_blocks = 0;
level_layout.z_extent = 0;
uint32_t packed_sublevel_last = is_base ? 0 : max_level;
for (uint32_t packed_sublevel = layout.packed_level;
packed_sublevel <= packed_sublevel_last; ++packed_sublevel) {
uint32_t packed_sublevel_x_blocks;
uint32_t packed_sublevel_y_blocks;
uint32_t packed_sublevel_z;
GetPackedMipOffset(width_texels, height_texels, depth, format,
packed_sublevel, packed_sublevel_x_blocks,
packed_sublevel_y_blocks, packed_sublevel_z);
level_layout.x_extent_blocks = std::max(
level_layout.x_extent_blocks,
packed_sublevel_x_blocks +
xe::align(
std::max(width_texels >> packed_sublevel, uint32_t(1)),
format_info->block_width) /
format_info->block_width);
level_layout.y_extent_blocks = std::max(
level_layout.y_extent_blocks,
packed_sublevel_y_blocks +
xe::align(
std::max(height_texels >> packed_sublevel, uint32_t(1)),
format_info->block_height) /
format_info->block_height);
level_layout.z_extent =
std::max(level_layout.z_extent,
packed_sublevel_z +
std::max(depth >> packed_sublevel, uint32_t(1)));
}
} else {
level_layout.x_extent_blocks =
xe::align(level_width_blocks, xenos::kTextureTileWidthHeight);
xe::align(std::max(width_texels >> level, uint32_t(1)),
format_info->block_width) /
format_info->block_width;
level_layout.y_extent_blocks =
xe::align(level_height_blocks, xenos::kTextureTileWidthHeight);
xe::align(std::max(height_texels >> level, uint32_t(1)),
format_info->block_height) /
format_info->block_height;
level_layout.z_extent = std::max(depth >> level, uint32_t(1));
}
if (is_tiled) {
uint32_t bytes_per_block_log2 = xe::log2_floor(bytes_per_block);
if (dimension == xenos::DataDimension::k3D) {
level_layout.z_extent =
xe::align(level_depth, xenos::kTextureTileDepth);
// 32-block-row x 4 slice portions laid out sequentially (4-slice-major,
// 32-block-row-minor), address extent within a 32x32x4 tile depends on
// the pitch. Origins of 32x32x4 tiles grow monotonically, first along
// Z, then along Y, then along X.
level_layout.array_slice_data_extent_bytes =
GetTiledAddressUpperBound3D(
level_layout.x_extent_blocks, level_layout.y_extent_blocks,
level_layout.z_extent, row_pitch_blocks_tile_aligned,
level_layout.y_extent_blocks, bytes_per_block_log2);
} else {
level_layout.z_extent = 1;
// Origins of 32x32 tiles grow monotonically, first along Y, then along
// X.
level_layout.array_slice_data_extent_bytes =
GetTiledAddressUpperBound2D(
level_layout.x_extent_blocks, level_layout.y_extent_blocks,
row_pitch_blocks_tile_aligned, bytes_per_block_log2);
}
} else {
if (level == layout.packed_level) {
// Calculate the portion of the mip tail actually used by the needed
// mips. The actually used region may be significantly smaller than the
// full 32x32-texel-aligned tail. A 2x2 texture (for example, in
// 494707D4, there's a 2x2 k_8_8_8_8 linear texture with packed mips),
// for instance, would have its 2x2 base at (16, 0) and its 1x1 mip at
// (8, 0) - and we need 2 or 1 rows in these cases, not 32 - the 32 rows
// would span two 4 KB pages rather than one, taking the 256-byte pitch
// alignment in linear textures into account.
level_layout.x_extent_blocks = 0;
level_layout.y_extent_blocks = 0;
level_layout.z_extent = 0;
uint32_t packed_sublevel_last = is_base ? 0 : max_level;
for (uint32_t packed_sublevel = layout.packed_level;
packed_sublevel <= packed_sublevel_last; ++packed_sublevel) {
uint32_t packed_sublevel_x_blocks;
uint32_t packed_sublevel_y_blocks;
uint32_t packed_sublevel_z;
GetPackedMipOffset(width_texels, height_texels, depth, format,
packed_sublevel, packed_sublevel_x_blocks,
packed_sublevel_y_blocks, packed_sublevel_z);
level_layout.x_extent_blocks = std::max<uint32_t>(
level_layout.x_extent_blocks,
packed_sublevel_x_blocks +
(xe::align<uint32_t>(
std::max(width_texels >> packed_sublevel, uint32_t(1)),
format_info->block_width) >>
block_width_sh));
level_layout.y_extent_blocks = std::max<uint32_t>(
level_layout.y_extent_blocks,
packed_sublevel_y_blocks +
(xe::align<uint32_t>(
std::max(height_texels >> packed_sublevel, uint32_t(1)),
format_info->block_height) >>
block_height_sh));
level_layout.z_extent =
std::max(level_layout.z_extent,
packed_sublevel_z +
std::max(depth >> packed_sublevel, uint32_t(1)));
}
} else {
level_layout.x_extent_blocks = level_width_blocks;
level_layout.y_extent_blocks = level_height_blocks;
level_layout.z_extent = level_depth;
}
level_layout.array_slice_data_extent_bytes =
z_stride_bytes * (level_layout.z_extent - 1) +
level_layout.row_pitch_bytes * (level_layout.y_extent_blocks - 1) +

View File

@@ -144,24 +144,27 @@ struct TextureGuestLayout {
// multiplied by the array slice count.
uint32_t array_slice_stride_bytes;
// Estimated amount of memory this level occupies, and variables involved in
// its calculation. Not aligned to kTextureSubresourceAlignmentBytes. For
// tiled textures, this will be rounded to 32x32x4 blocks (or 32x32x1
// depending on the dimension), but for the linear subresources, this may be
// significantly (including less 4 KB pages) smaller than the aligned size
// (like for 4E4D083E where aligning the height of a 1280x720 linear texture
// results in access violations). For the linear mip tail, this includes all
// the mip levels stored in it. If the width is bigger than the pitch, this
// will also be taken into account for the last row so all memory actually
// used by the texture will be loaded, and may be bigger than the distance
// between array slices or levels. The purpose of this parameter is to make
// the memory amount that needs to be resident as close to the real amount
// as possible, to make sure all the needed data will be read, but also, if
// possible, unneeded memory pages won't be accessed (since that may trigger
// an access violation on the CPU).
// The exclusive upper bound of blocks needed at this level (this level for
// non-packed levels, or all the packed levels for the packed mip tail).
uint32_t x_extent_blocks;
uint32_t y_extent_blocks;
uint32_t z_extent;
// Estimated amount of memory this level occupies. Not aligned to
// kTextureSubresourceAlignmentBytes. For tiled textures, this will be
// calculated for the extent rounded to 32x32x4 blocks (or 32x32x1 depending
// on the dimensionality), but for linear textures, as well as for mips of
// non-power-of-two tiled textures, this may be significantly (including
// less 4 KB pages) smaller than the aligned size (like for 4E4D083E where
// aligning the height of a 1280x720 linear texture results in access
// violations). For the linear mip tail, this includes all the mip levels
// stored in it. If the width is bigger than the pitch, this will also be
// taken into account for the last row so all memory actually used by the
// texture will be loaded, and may be bigger than the distance between array
// slices or levels. The purpose of this parameter is to make the memory
// amount that needs to be resident as close to the real amount as possible,
// to make sure all the needed data will be read, but also, if possible,
// unneeded memory pages won't be accessed (since that may trigger an access
// violation on the CPU).
uint32_t array_slice_data_extent_bytes;
// Including all array slices.
uint32_t level_data_extent_bytes;

View File

@@ -258,7 +258,7 @@ void TraceViewer::DrawControllerUI() {
}
ImGui::SameLine();
ImGui::SliderInt("", &target_frame, 0, player_->frame_count() - 1);
ImGui::SliderInt("##", &target_frame, 0, player_->frame_count() - 1);
if (target_frame != player_->current_frame_index() &&
!player_->is_playing_trace()) {
player_->SeekFrame(target_frame);
@@ -554,7 +554,7 @@ void TraceViewer::DrawCommandListUI() {
}
ImGui::PushItemWidth(float(column_width - 15));
ImGui::SliderInt("", &target_command, -1, command_count - 1);
ImGui::SliderInt("##", &target_command, -1, command_count - 1);
ImGui::PopItemWidth();
if (target_command != player_->current_command_index() &&
@@ -733,8 +733,8 @@ void TraceViewer::DrawTextureInfo(
ImGui::Columns(2);
if (texture) {
ImVec2 button_size(256, 256);
if (ImGui::ImageButton(ImTextureID(texture), button_size, ImVec2(0, 0),
ImVec2(1, 1))) {
if (ImGui::ImageButton("#texture_info_image", ImTextureID(texture),
button_size, ImVec2(0, 0), ImVec2(1, 1))) {
// show viewer
}
} else {
@@ -818,8 +818,7 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
int display_start, display_end;
ImGui::CalcListClipping(vertex_count, ImGui::GetTextLineHeight(),
&display_start, &display_end);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
(display_start)*ImGui::GetTextLineHeight());
ImGui::Dummy(ImVec2(0, (display_start)*ImGui::GetTextLineHeight()));
ImGui::Columns(column_count);
if (display_start <= 1) {
for (size_t el_index = 0; el_index < vertex_binding.attributes.size();
@@ -1005,8 +1004,8 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
}
}
ImGui::Columns(1);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (vertex_count - display_end) *
ImGui::GetTextLineHeight());
ImGui::Dummy(
ImVec2(0, (vertex_count - display_end) * ImGui::GetTextLineHeight()));
ImGui::PopStyleVar();
ImGui::EndChild();
}
@@ -1463,19 +1462,21 @@ void TraceViewer::DrawStateUI() {
ImVec2 button_pos = ImGui::GetCursorScreenPos();
ImVec2 button_size(256, 256);
ImTextureID tex = 0;
ImGui::PushID(i);
if (write_mask) {
auto color_target = GetColorRenderTarget(surface_pitch, surface_msaa,
color_base, color_format);
tex = ImTextureID(color_target);
if (ImGui::ImageButton(tex, button_size, ImVec2(0, 0),
if (ImGui::ImageButton("#color_image", tex, button_size, ImVec2(0, 0),
ImVec2(1, 1))) {
// show viewer
}
} else {
ImGui::ImageButton(ImTextureID(0), button_size, ImVec2(0, 0),
ImVec2(1, 1), -1, ImVec4(0, 0, 0, 0),
ImGui::ImageButton("#color_image", ImTextureID(0), button_size,
ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0),
ImVec4(0, 0, 0, 0));
}
ImGui::PopID();
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("Color Target %d (%s), base %.4X, pitch %d, format %s", i,
@@ -1586,8 +1587,8 @@ void TraceViewer::DrawStateUI() {
auto button_pos = ImGui::GetCursorScreenPos();
ImVec2 button_size(256, 256);
ImGui::ImageButton(ImTextureID(depth_target), button_size, ImVec2(0, 0),
ImVec2(1, 1));
ImGui::ImageButton("#depth_stencil_image", ImTextureID(depth_target),
button_size, ImVec2(0, 0), ImVec2(1, 1));
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
@@ -1640,8 +1641,7 @@ void TraceViewer::DrawStateUI() {
ImGui::CalcListClipping(int(vertices.size() / 4),
ImGui::GetTextLineHeight(), &display_start,
&display_end);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
(display_start)*ImGui::GetTextLineHeight());
ImGui::Dummy(ImVec2(0, (display_start)*ImGui::GetTextLineHeight()));
ImGui::Columns(int(el_size), "#vsvertices", true);
for (size_t i = display_start; i < display_end; i++) {
@@ -1662,9 +1662,8 @@ void TraceViewer::DrawStateUI() {
}
ImGui::Columns(1);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
((vertices.size() / 4) - display_end) *
ImGui::GetTextLineHeight());
ImGui::Dummy(ImVec2(0, ((vertices.size() / 4) - display_end) *
ImGui::GetTextLineHeight()));
ImGui::EndChild();
} else {
ImGui::Text("No vertex shader output");
@@ -1708,8 +1707,7 @@ void TraceViewer::DrawStateUI() {
ImGui::CalcListClipping(1 + draw_info.index_count,
ImGui::GetTextLineHeight(), &display_start,
&display_end);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
(display_start)*ImGui::GetTextLineHeight());
ImGui::Dummy(ImVec2(0, (display_start)*ImGui::GetTextLineHeight()));
ImGui::Columns(2, "#indices", true);
ImGui::SetColumnOffset(1, 60);
if (display_start <= 1) {
@@ -1744,9 +1742,8 @@ void TraceViewer::DrawStateUI() {
ImGui::NextColumn();
}
ImGui::Columns(1);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
(draw_info.index_count - display_end) *
ImGui::GetTextLineHeight());
ImGui::Dummy(ImVec2(0, (draw_info.index_count - display_end) *
ImGui::GetTextLineHeight()));
ImGui::PopStyleVar();
ImGui::EndChild();
}

View File

@@ -2546,8 +2546,6 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
// After all commands that may dispatch, copy or insert barriers, submit the
// barriers (may end the render pass), and (re)enter the render pass before
// drawing.
// TODO(Triang3l): Handle disabled variableMultisampleRate by restarting the
// render pass with no attachments if the sample count becomes different.
SubmitBarriersAndEnterRenderTargetCacheRenderPass(
render_target_cache_->last_update_render_pass(),
render_target_cache_->last_update_framebuffer());

View File

@@ -581,6 +581,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
primitive_processing_result.host_primitive_reset_enabled;
description_out.depth_clamp_enable =
device_features.depthClamp &&
regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable;
// TODO(Triang3l): Tessellation.
@@ -825,6 +826,10 @@ bool VulkanPipelineCache::ArePipelineRequirementsMet(
return false;
}
if (!device_features.depthClamp && description.depth_clamp_enable) {
return false;
}
if (!device_features.fillModeNonSolid &&
description.polygon_mode != PipelinePolygonMode::kFill) {
return false;
@@ -1173,9 +1178,6 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
builder.makeStructType(id_vector_temp, "gl_PerVertex");
builder.addMemberName(type_struct_out_gl_per_vertex,
member_out_gl_per_vertex_position, "gl_Position");
builder.addMemberDecoration(type_struct_out_gl_per_vertex,
member_out_gl_per_vertex_position,
spv::DecorationInvariant);
builder.addMemberDecoration(type_struct_out_gl_per_vertex,
member_out_gl_per_vertex_position,
spv::DecorationBuiltIn, spv::BuiltInPosition);
@@ -1183,9 +1185,6 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
builder.addMemberName(type_struct_out_gl_per_vertex,
member_out_gl_per_vertex_clip_distance,
"gl_ClipDistance");
builder.addMemberDecoration(type_struct_out_gl_per_vertex,
member_out_gl_per_vertex_clip_distance,
spv::DecorationInvariant);
builder.addMemberDecoration(
type_struct_out_gl_per_vertex, member_out_gl_per_vertex_clip_distance,
spv::DecorationBuiltIn, spv::BuiltInClipDistance);
@@ -1194,6 +1193,7 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
spv::Id out_gl_per_vertex =
builder.createVariable(spv::NoPrecision, spv::StorageClassOutput,
type_struct_out_gl_per_vertex, "");
builder.addDecoration(out_gl_per_vertex, spv::DecorationInvariant);
main_interface.push_back(out_gl_per_vertex);
// Begin the main function.

View File

@@ -44,9 +44,13 @@ class VulkanRenderTargetCache final : public RenderTargetCache {
// targets are different for 2x and 4x guest MSAA, pipelines because the
// sample mask will have 2 samples excluded for 2x-as-4x).
// This has effect only on the attachments, but even in cases when there
// are no attachments, it can be used to the sample count between
// are no attachments, it can be used to pass the sample count between
// subsystems, for instance, to specify the desired number of samples to
// use when there are no attachments in pipelines.
// Also, without attachments, using separate render passes for different
// sample counts ensures that if the variableMultisampleRate feature is
// not supported, no draws with different rasterization sample counts end
// up in one render pass.
xenos::MsaaSamples msaa_samples : xenos::kMsaaSamplesBits; // 2
// << 0 is depth, << 1...4 is color.
uint32_t depth_and_color_used : 1 + xenos::kMaxColorRenderTargets; // 7

View File

@@ -32,6 +32,10 @@ bool SDLHelper::Prepare() {
is_prepared_ &= SetHints();
is_prepared_ &= RedirectLog();
SDL_version ver = {};
SDL_GetVersion(&ver);
XELOGI("SDL Version {}.{}.{} initialized.", ver.major, ver.minor, ver.patch);
return is_prepared_;
}

View File

@@ -69,12 +69,12 @@ std::string XdbfWrapper::GetStringTableEntry(XLanguage language,
}
auto xstr_head =
reinterpret_cast<const XdbfXstrHeader*>(language_block.buffer);
reinterpret_cast<const XdbfSectionHeader*>(language_block.buffer);
assert_true(xstr_head->magic == kXdbfSignatureXstr);
assert_true(xstr_head->version == 1);
const uint8_t* ptr = language_block.buffer + sizeof(XdbfXstrHeader);
for (uint16_t i = 0; i < xstr_head->string_count; ++i) {
const uint8_t* ptr = language_block.buffer + sizeof(XdbfSectionHeader);
for (uint16_t i = 0; i < xstr_head->count; ++i) {
auto entry = reinterpret_cast<const XdbfStringTableEntry*>(ptr);
ptr += sizeof(XdbfStringTableEntry);
if (entry->id == string_id) {
@@ -94,19 +94,18 @@ std::vector<XdbfAchievementTableEntry> XdbfWrapper::GetAchievements() const {
return achievements;
}
auto xstr_head =
reinterpret_cast<const XdbfXstrHeader*>(achievement_table.buffer);
assert_true(xstr_head->magic == kXdbfSignatureXach);
assert_true(xstr_head->version == 1);
auto xach_head =
reinterpret_cast<const XdbfSectionHeader*>(achievement_table.buffer);
assert_true(xach_head->magic == kXdbfSignatureXach);
assert_true(xach_head->version == 1);
const uint8_t* ptr = achievement_table.buffer + sizeof(XdbfXstrHeader);
for (uint16_t i = 0; i < xstr_head->string_count; ++i) {
const uint8_t* ptr = achievement_table.buffer + sizeof(XdbfSectionHeader);
for (uint16_t i = 0; i < xach_head->count; ++i) {
auto entry = reinterpret_cast<const XdbfAchievementTableEntry*>(ptr);
ptr += sizeof(XdbfAchievementTableEntry);
achievements.push_back(*entry);
}
return achievements;
}
XLanguage XdbfGameData::GetExistingLanguage(XLanguage language_to_check) const {

View File

@@ -62,13 +62,13 @@ struct XdbfXstc {
};
static_assert_size(XdbfXstc, 16);
struct XdbfXstrHeader {
struct XdbfSectionHeader {
xe::be<uint32_t> magic;
xe::be<uint32_t> version;
xe::be<uint32_t> size;
xe::be<uint16_t> string_count;
xe::be<uint16_t> count;
};
static_assert_size(XdbfXstrHeader, 14);
static_assert_size(XdbfSectionHeader, 14);
struct XdbfStringTableEntry {
xe::be<uint16_t> id;

View File

@@ -719,14 +719,15 @@ dword_result_t XamUserCreateAchievementEnumerator_entry(
return result;
}
const kernel::util::XdbfGameData db = kernel_state()->title_xdbf();
const util::XdbfGameData db = kernel_state()->title_xdbf();
if (db.is_valid()) {
XLanguage language = db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
std::vector<kernel::util::XdbfAchievementTableEntry> achievement_list =
const XLanguage language =
db.GetExistingLanguage(static_cast<XLanguage>(cvars::user_language));
const std::vector<util::XdbfAchievementTableEntry> achievement_list =
db.GetAchievements();
for (const auto& entry : achievement_list) {
for (const util::XdbfAchievementTableEntry& entry : achievement_list) {
auto item = XStaticAchievementEnumerator::AchievementDetails{
entry.id,
xe::to_utf16(db.GetStringTableEntry(language, entry.label_id)),

View File

@@ -148,14 +148,16 @@ bool Win32FilePicker::Show(Window* parent_window) {
if (type() == Type::kFile) {
// Set the file types to display only. Notice that this is a 1-based array.
std::vector<std::pair<std::u16string, std::u16string>> file_pairs;
using u16sPair = std::pair<std::u16string, std::u16string>;
std::vector<std::unique_ptr<u16sPair>> file_pairs;
std::vector<COMDLG_FILTERSPEC> file_types;
for (const auto& extension : this->extensions()) {
const auto& file_pair =
file_pairs.emplace_back(std::move(xe::to_utf16(extension.first)),
std::move(xe::to_utf16(extension.second)));
file_types.push_back({(LPCWSTR)file_pair.first.c_str(),
(LPCWSTR)file_pair.second.c_str()});
file_pairs.emplace_back(std::make_unique<u16sPair>(
xe::to_utf16(extension.first), xe::to_utf16(extension.second)));
file_types.push_back(
{reinterpret_cast<LPCWSTR>(file_pair->first.c_str()),
reinterpret_cast<LPCWSTR>(file_pair->second.c_str())});
}
hr = file_dialog->SetFileTypes(static_cast<UINT>(file_types.size()),
file_types.data());

View File

@@ -62,11 +62,11 @@ void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) {
dialogs_.cend()) {
return;
}
if (dialog_loop_next_index_ == SIZE_MAX && dialogs_.empty()) {
// First dialog added. dialog_loop_next_index_ == SIZE_MAX is also checked
// because in a situation of removing the only dialog, then adding a dialog,
// from within a dialog's Draw function, the removal would not cause the
// listener and the drawer to be removed (it's deferred in this case).
if (dialogs_.empty() && !IsDrawingDialogs()) {
// First dialog added. !IsDrawingDialogs() is also checked because in a
// situation of removing the only dialog, then adding a dialog, from within
// a dialog's Draw function, re-registering the ImGuiDrawer may result in
// ImGui being drawn multiple times in the current frame.
window_->AddInputListener(this, z_order_);
if (presenter_) {
presenter_->AddUIDrawerFromUIThread(this, z_order_);
@@ -81,7 +81,7 @@ void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) {
if (it == dialogs_.cend()) {
return;
}
if (dialog_loop_next_index_ != SIZE_MAX) {
if (IsDrawingDialogs()) {
// Actualize the next dialog index after the erasure from the vector.
size_t existing_index = size_t(std::distance(dialogs_.cbegin(), it));
if (dialog_loop_next_index_ > existing_index) {
@@ -89,17 +89,7 @@ void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) {
}
}
dialogs_.erase(it);
if (dialog_loop_next_index_ == SIZE_MAX && dialogs_.empty()) {
if (presenter_) {
presenter_->RemoveUIDrawerFromUIThread(this);
}
window_->RemoveInputListener(this);
// Clear all input since no input will be received anymore, and when the
// drawer becomes active again, it'd have an outdated input state otherwise
// which will be persistent until new events actualize individual input
// properties.
ClearInput();
}
DetachIfLastDialogRemoved();
}
void ImGuiDrawer::Initialize() {
@@ -194,24 +184,6 @@ void ImGuiDrawer::Initialize() {
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 1.00f, 0.00f, 0.21f);
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
io.KeyMap[ImGuiKey_Tab] = int(ui::VirtualKey::kTab);
io.KeyMap[ImGuiKey_LeftArrow] = int(ui::VirtualKey::kLeft);
io.KeyMap[ImGuiKey_RightArrow] = int(ui::VirtualKey::kRight);
io.KeyMap[ImGuiKey_UpArrow] = int(ui::VirtualKey::kUp);
io.KeyMap[ImGuiKey_DownArrow] = int(ui::VirtualKey::kDown);
io.KeyMap[ImGuiKey_Home] = int(ui::VirtualKey::kHome);
io.KeyMap[ImGuiKey_End] = int(ui::VirtualKey::kEnd);
io.KeyMap[ImGuiKey_Delete] = int(ui::VirtualKey::kDelete);
io.KeyMap[ImGuiKey_Backspace] = int(ui::VirtualKey::kBack);
io.KeyMap[ImGuiKey_Enter] = int(ui::VirtualKey::kReturn);
io.KeyMap[ImGuiKey_Escape] = int(ui::VirtualKey::kEscape);
io.KeyMap[ImGuiKey_A] = int(ui::VirtualKey::kA);
io.KeyMap[ImGuiKey_C] = int(ui::VirtualKey::kC);
io.KeyMap[ImGuiKey_V] = int(ui::VirtualKey::kV);
io.KeyMap[ImGuiKey_X] = int(ui::VirtualKey::kX);
io.KeyMap[ImGuiKey_Y] = int(ui::VirtualKey::kY);
io.KeyMap[ImGuiKey_Z] = int(ui::VirtualKey::kZ);
frame_time_tick_frequency_ = double(Clock::QueryHostTickFrequency());
last_frame_time_ticks_ = Clock::QueryHostTickCount();
@@ -219,6 +191,33 @@ void ImGuiDrawer::Initialize() {
reset_mouse_position_after_next_frame_ = false;
}
std::optional<ImGuiKey> ImGuiDrawer::VirtualKeyToImGuiKey(VirtualKey vkey) {
static const std::map<VirtualKey, ImGuiKey> map = {
{ui::VirtualKey::kTab, ImGuiKey_Tab},
{ui::VirtualKey::kLeft, ImGuiKey_LeftArrow},
{ui::VirtualKey::kRight, ImGuiKey_RightArrow},
{ui::VirtualKey::kUp, ImGuiKey_UpArrow},
{ui::VirtualKey::kDown, ImGuiKey_DownArrow},
{ui::VirtualKey::kHome, ImGuiKey_Home},
{ui::VirtualKey::kEnd, ImGuiKey_End},
{ui::VirtualKey::kDelete, ImGuiKey_Delete},
{ui::VirtualKey::kBack, ImGuiKey_Backspace},
{ui::VirtualKey::kReturn, ImGuiKey_Enter},
{ui::VirtualKey::kEscape, ImGuiKey_Escape},
{ui::VirtualKey::kA, ImGuiKey_A},
{ui::VirtualKey::kC, ImGuiKey_C},
{ui::VirtualKey::kV, ImGuiKey_V},
{ui::VirtualKey::kX, ImGuiKey_X},
{ui::VirtualKey::kY, ImGuiKey_Y},
{ui::VirtualKey::kZ, ImGuiKey_Z},
};
if (auto search = map.find(vkey); search != map.end()) {
return search->second;
} else {
return std::nullopt;
}
}
void ImGuiDrawer::SetupFontTexture() {
if (font_texture_ || !immediate_drawer_) {
return;
@@ -301,7 +300,7 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
ImGui::NewFrame();
assert_true(dialog_loop_next_index_ == SIZE_MAX);
assert_true(!IsDrawingDialogs());
dialog_loop_next_index_ = 0;
while (dialog_loop_next_index_ < dialogs_.size()) {
dialogs_[dialog_loop_next_index_++]->Draw();
@@ -319,11 +318,11 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
}
if (dialogs_.empty()) {
// All dialogs have removed themselves during the draw, detach.
presenter_->RemoveUIDrawerFromUIThread(this);
window_->RemoveInputListener(this);
} else {
// Detaching is deferred if the last dialog is removed during drawing, perform
// it now if needed.
DetachIfLastDialogRemoved();
if (!dialogs_.empty()) {
// Repaint (and handle input) continuously if still active.
presenter_->RequestUIPaintFromUIThread();
}
@@ -346,14 +345,13 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data,
batch.index_count = cmd_list->IdxBuffer.size();
immediate_drawer_->BeginDrawBatch(batch);
int index_offset = 0;
for (int j = 0; j < cmd_list->CmdBuffer.size(); ++j) {
const auto& cmd = cmd_list->CmdBuffer[j];
ImmediateDraw draw;
draw.primitive_type = ImmediatePrimitiveType::kTriangles;
draw.count = cmd.ElemCount;
draw.index_offset = index_offset;
draw.index_offset = cmd.IdxOffset;
draw.texture = reinterpret_cast<ImmediateTexture*>(cmd.TextureId);
draw.scissor = true;
draw.scissor_left = cmd.ClipRect.x;
@@ -361,8 +359,6 @@ void ImGuiDrawer::RenderDrawLists(ImDrawData* data,
draw.scissor_right = cmd.ClipRect.z;
draw.scissor_bottom = cmd.ClipRect.w;
immediate_drawer_->Draw(draw);
index_offset += cmd.ElemCount;
}
immediate_drawer_->EndDrawBatch();
@@ -497,13 +493,7 @@ void ImGuiDrawer::ClearInput() {
}
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
std::memset(io.MouseDown, 0, sizeof(io.MouseDown));
io.MouseWheel = 0.0f;
io.MouseWheelH = 0.0f;
io.KeyCtrl = false;
io.KeyShift = false;
io.KeyAlt = false;
io.KeySuper = false;
std::memset(io.KeysDown, 0, sizeof(io.KeysDown));
io.ClearInputKeys();
io.ClearInputCharacters();
touch_pointer_id_ = TouchEvent::kPointerIDNone;
reset_mouse_position_after_next_frame_ = false;
@@ -511,9 +501,9 @@ void ImGuiDrawer::ClearInput() {
void ImGuiDrawer::OnKey(KeyEvent& e, bool is_down) {
auto& io = GetIO();
VirtualKey virtual_key = e.virtual_key();
if (size_t(virtual_key) < xe::countof(io.KeysDown)) {
io.KeysDown[size_t(virtual_key)] = is_down;
const VirtualKey virtual_key = e.virtual_key();
if (auto imGuiKey = VirtualKeyToImGuiKey(virtual_key); imGuiKey) {
io.AddKeyEvent(*imGuiKey, is_down);
}
switch (virtual_key) {
case VirtualKey::kShift:
@@ -557,5 +547,24 @@ void ImGuiDrawer::SwitchToPhysicalMouseAndUpdateMousePosition(
UpdateMousePosition(float(e.x()), float(e.y()));
}
void ImGuiDrawer::DetachIfLastDialogRemoved() {
// IsDrawingDialogs() is also checked because in a situation of removing the
// only dialog, then adding a dialog, from within a dialog's Draw function,
// re-registering the ImGuiDrawer may result in ImGui being drawn multiple
// times in the current frame.
if (!dialogs_.empty() || IsDrawingDialogs()) {
return;
}
if (presenter_) {
presenter_->RemoveUIDrawerFromUIThread(this);
}
window_->RemoveInputListener(this);
// Clear all input since no input will be received anymore, and when the
// drawer becomes active again, it'd have an outdated input state otherwise
// which will be persistent until new events actualize individual input
// properties.
ClearInput();
}
} // namespace ui
} // namespace xe

View File

@@ -23,6 +23,7 @@
struct ImDrawData;
struct ImGuiContext;
struct ImGuiIO;
enum ImGuiKey : int;
namespace xe {
namespace ui {
@@ -74,6 +75,11 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
void UpdateMousePosition(float x, float y);
void SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e);
bool IsDrawingDialogs() const { return dialog_loop_next_index_ != SIZE_MAX; }
void DetachIfLastDialogRemoved();
std::optional<ImGuiKey> VirtualKeyToImGuiKey(VirtualKey vkey);
Window* window_;
size_t z_order_;