diff --git a/premake5.lua b/premake5.lua index 94df2fde5..7c429828b 100644 --- a/premake5.lua +++ b/premake5.lua @@ -183,6 +183,7 @@ solution("xenia") include("src/xenia/debug/ui") include("src/xenia/gpu") include("src/xenia/gpu/gl4") + include("src/xenia/gpu/vulkan") include("src/xenia/hid") include("src/xenia/hid/nop") include("src/xenia/kernel") diff --git a/src/xenia/cpu/hir/value.cc b/src/xenia/cpu/hir/value.cc index 0c6f62f55..dc7e8cf64 100644 --- a/src/xenia/cpu/hir/value.cc +++ b/src/xenia/cpu/hir/value.cc @@ -1023,6 +1023,14 @@ void Value::VectorSub(Value* other, TypeName type, bool is_unsigned, } } } + break; + case FLOAT32_TYPE: + assert_false(is_unsigned); + assert_false(saturate); + for (int i = 0; i < 4; i++) { + constant.v128.f32[i] -= other->constant.v128.f32[i]; + } + break; default: assert_unhandled_case(type); break; diff --git a/src/xenia/gpu/trace_viewer.cc b/src/xenia/gpu/trace_viewer.cc index 317675709..7ce20c7ca 100644 --- a/src/xenia/gpu/trace_viewer.cc +++ b/src/xenia/gpu/trace_viewer.cc @@ -1439,7 +1439,8 @@ void TraceViewer::DrawStateUI() { } ImGui::EndChild(); } - if (ImGui::CollapsingHeader("Vertex Shader Output")) { + if (ImGui::CollapsingHeader("Vertex Shader Output") && + QueryVSOutputElementSize()) { auto size = QueryVSOutputSize(); auto el_size = QueryVSOutputElementSize(); if (size > 0) { diff --git a/src/xenia/gpu/trace_viewer.h b/src/xenia/gpu/trace_viewer.h index 5a5c1b104..6f7c900fc 100644 --- a/src/xenia/gpu/trace_viewer.h +++ b/src/xenia/gpu/trace_viewer.h @@ -54,9 +54,9 @@ class TraceViewer { virtual uintptr_t GetTextureEntry(const TextureInfo& texture_info, const SamplerInfo& sampler_info) = 0; - virtual size_t QueryVSOutputSize() = 0; - virtual size_t QueryVSOutputElementSize() = 0; - virtual bool QueryVSOutput(void* buffer, size_t size) = 0; + virtual size_t QueryVSOutputSize() { return 0; } + virtual size_t QueryVSOutputElementSize() { return 0; } + virtual bool QueryVSOutput(void* buffer, size_t size) { return false; } virtual bool Setup(); diff --git a/src/xenia/gpu/vulkan/premake5.lua b/src/xenia/gpu/vulkan/premake5.lua new file mode 100644 index 000000000..5a89101e2 --- /dev/null +++ b/src/xenia/gpu/vulkan/premake5.lua @@ -0,0 +1,125 @@ +project_root = "../../../.." +include(project_root.."/tools/build") + +group("src") +project("xenia-gpu-vulkan") + uuid("717590b4-f579-4162-8f23-0624e87d6cca") + kind("StaticLib") + language("C++") + links({ + "vulkan-loader", + "xenia-base", + "xenia-gpu", + "xenia-ui", + "xenia-ui-spirv", + "xenia-ui-vulkan", + "xxhash", + }) + defines({ + }) + includedirs({ + project_root.."/third_party/gflags/src", + }) + local_platform_files() + +-- TODO(benvanik): kill this and move to the debugger UI. +group("src") +project("xenia-gpu-vulkan-trace-viewer") + uuid("86a1dddc-a26a-4885-8c55-cf745225d93e") + kind("WindowedApp") + language("C++") + links({ + "gflags", + "imgui", + "vulkan-loader", + "xenia-apu", + "xenia-apu-nop", + "xenia-apu-xaudio2", + "xenia-base", + "xenia-core", + "xenia-cpu", + "xenia-cpu-backend-x64", + "xenia-gpu", + "xenia-gpu-vulkan", + "xenia-hid-nop", + "xenia-hid-winkey", + "xenia-hid-xinput", + "xenia-kernel", + "xenia-ui", + "xenia-ui-spirv", + "xenia-ui-vulkan", + "xenia-vfs", + }) + flags({ + "WinMain", -- Use WinMain instead of main. + }) + defines({ + }) + includedirs({ + project_root.."/third_party/gflags/src", + }) + files({ + "vulkan_trace_viewer_main.cc", + "../../base/main_"..platform_suffix..".cc", + }) + + filter("platforms:Windows") + -- Only create the .user file if it doesn't already exist. + local user_file = project_root.."/build/xenia-gpu-vulkan-trace-viewer.vcxproj.user" + if not os.isfile(user_file) then + debugdir(project_root) + debugargs({ + "--flagfile=scratch/flags.txt", + "2>&1", + "1>scratch/stdout-trace-viewer.txt", + }) + end + +group("src") +project("xenia-gpu-vulkan-trace-dump") + uuid("0dd0dd1c-b321-494d-ab9a-6c062f0c65cc") + kind("ConsoleApp") + language("C++") + links({ + "gflags", + "imgui", + "vulkan-loader", + "xenia-apu", + "xenia-apu-nop", + "xenia-apu-xaudio2", + "xenia-base", + "xenia-core", + "xenia-cpu", + "xenia-cpu-backend-x64", + "xenia-gpu", + "xenia-gpu-vulkan", + "xenia-hid-nop", + "xenia-hid-winkey", + "xenia-hid-xinput", + "xenia-kernel", + "xenia-ui", + "xenia-ui-spirv", + "xenia-ui-vulkan", + "xenia-vfs", + }) + defines({ + }) + includedirs({ + project_root.."/third_party/gflags/src", + }) + files({ + "vulkan_trace_dump_main.cc", + "../../base/main_"..platform_suffix..".cc", + }) + + filter("platforms:Windows") + -- Only create the .user file if it doesn't already exist. + local user_file = project_root.."/build/xenia-gpu-vulkan-trace-dump.vcxproj.user" + if not os.isfile(user_file) then + debugdir(project_root) + debugargs({ + "--flagfile=scratch/flags.txt", + "2>&1", + "1>scratch/stdout-trace-dump.txt", + }) + end diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc new file mode 100644 index 000000000..3320d2927 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -0,0 +1,585 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/vulkan/vulkan_command_processor.h" + +#include + +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/profiling.h" +#include "xenia/gpu/gpu_flags.h" +#include "xenia/gpu/sampler_info.h" +#include "xenia/gpu/texture_info.h" +#include "xenia/gpu/vulkan/vulkan_gpu_flags.h" +#include "xenia/gpu/vulkan/vulkan_graphics_system.h" +#include "xenia/gpu/xenos.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +using namespace xe::gpu::xenos; + +VulkanCommandProcessor::VulkanCommandProcessor( + VulkanGraphicsSystem* graphics_system, kernel::KernelState* kernel_state) + : CommandProcessor(graphics_system, kernel_state) {} + +VulkanCommandProcessor::~VulkanCommandProcessor() = default; + +void VulkanCommandProcessor::ClearCaches() { CommandProcessor::ClearCaches(); } + +bool VulkanCommandProcessor::SetupContext() { + if (!CommandProcessor::SetupContext()) { + XELOGE("Unable to initialize base command processor context"); + return false; + } + + return true; +} + +void VulkanCommandProcessor::ShutdownContext() { + CommandProcessor::ShutdownContext(); +} + +void VulkanCommandProcessor::MakeCoherent() { + RegisterFile* regs = register_file_; + auto status_host = regs->values[XE_GPU_REG_COHER_STATUS_HOST].u32; + + CommandProcessor::MakeCoherent(); + + if (status_host & 0x80000000ul) { + // scratch_buffer_.ClearCache(); + } +} + +void VulkanCommandProcessor::PrepareForWait() { + SCOPE_profile_cpu_f("gpu"); + + CommandProcessor::PrepareForWait(); + + // TODO(benvanik): fences and fancy stuff. We should figure out a way to + // make interrupt callbacks from the GPU so that we don't have to do a full + // synchronize here. + // glFlush(); + // glFinish(); + + context_->ClearCurrent(); +} + +void VulkanCommandProcessor::ReturnFromWait() { + context_->MakeCurrent(); + + CommandProcessor::ReturnFromWait(); +} + +void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, + uint32_t frontbuffer_width, + uint32_t frontbuffer_height) { + // Ensure we issue any pending draws. + // draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); + + // Need to finish to be sure the other context sees the right data. + // TODO(benvanik): prevent this? fences? + // glFinish(); + + if (context_->WasLost()) { + // We've lost the context due to a TDR. + // TODO: Dump the current commands to a tracefile. + assert_always(); + } + + // Remove any dead textures, etc. + // texture_cache_.Scavenge(); +} + +Shader* VulkanCommandProcessor::LoadShader(ShaderType shader_type, + uint32_t guest_address, + const uint32_t* host_address, + uint32_t dword_count) { + // return shader_cache_.LookupOrInsertShader(shader_type, host_address, + // dword_count); + return nullptr; +} + +bool VulkanCommandProcessor::IssueDraw(PrimitiveType prim_type, + uint32_t index_count, + IndexBufferInfo* index_buffer_info) { +#if FINE_GRAINED_DRAW_SCOPES + SCOPE_profile_cpu_f("gpu"); +#endif // FINE_GRAINED_DRAW_SCOPES + + // Skip all drawing for now - what did you expect? :) + return true; + + bool draw_valid = false; + // if (index_buffer_info) { + // draw_valid = draw_batcher_.BeginDrawElements(prim_type, index_count, + // index_buffer_info->format); + //} else { + // draw_valid = draw_batcher_.BeginDrawArrays(prim_type, index_count); + //} + if (!draw_valid) { + return false; + } + + auto& regs = *register_file_; + + auto enable_mode = + static_cast(regs[XE_GPU_REG_RB_MODECONTROL].u32 & 0x7); + if (enable_mode == ModeControl::kIgnore) { + // Ignored. + // draw_batcher_.DiscardDraw(); + return true; + } else if (enable_mode == ModeControl::kCopy) { + // Special copy handling. + // draw_batcher_.DiscardDraw(); + return IssueCopy(); + } + +#define CHECK_ISSUE_UPDATE_STATUS(status, mismatch, error_message) \ + { \ + if (status == UpdateStatus::kError) { \ + XELOGE(error_message); \ + /*draw_batcher_.DiscardDraw(); */ \ + return false; \ + } else if (status == UpdateStatus::kMismatch) { \ + mismatch = true; \ + } \ + } + + UpdateStatus status; + bool mismatch = false; + status = UpdateShaders(prim_type); + CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to prepare draw shaders"); + status = UpdateRenderTargets(); + CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render targets"); + // if (!active_framebuffer_) { + // // No framebuffer, so nothing we do will actually have an effect. + // // Treat it as a no-op. + // // TODO(benvanik): if we have a vs export, still allow it to go. + // draw_batcher_.DiscardDraw(); + // return true; + //} + + status = UpdateState(prim_type); + CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup render state"); + status = PopulateSamplers(); + CHECK_ISSUE_UPDATE_STATUS(status, mismatch, + "Unable to prepare draw samplers"); + + status = PopulateIndexBuffer(index_buffer_info); + CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup index buffer"); + status = PopulateVertexBuffers(); + CHECK_ISSUE_UPDATE_STATUS(status, mismatch, "Unable to setup vertex buffers"); + + // if (!draw_batcher_.CommitDraw()) { + // return false; + //} + + // draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); + if (context_->WasLost()) { + // This draw lost us the context. This typically isn't hit. + assert_always(); + return false; + } + + return true; +} + +bool VulkanCommandProcessor::SetShadowRegister(uint32_t* dest, + uint32_t register_name) { + uint32_t value = register_file_->values[register_name].u32; + if (*dest == value) { + return false; + } + *dest = value; + return true; +} + +bool VulkanCommandProcessor::SetShadowRegister(float* dest, + uint32_t register_name) { + float value = register_file_->values[register_name].f32; + if (*dest == value) { + return false; + } + *dest = value; + return true; +} + +VulkanCommandProcessor::UpdateStatus VulkanCommandProcessor::UpdateShaders( + PrimitiveType prim_type) { + auto& regs = update_shaders_regs_; + + // These are the constant base addresses/ranges for shaders. + // We have these hardcoded right now cause nothing seems to differ. + assert_true(register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == + 0x000FF000 || + register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == 0x00000000); + assert_true(register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == + 0x000FF100 || + register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == 0x00000000); + + bool dirty = false; + dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, + XE_GPU_REG_PA_SU_SC_MODE_CNTL); + dirty |= SetShadowRegister(®s.sq_program_cntl, XE_GPU_REG_SQ_PROGRAM_CNTL); + dirty |= SetShadowRegister(®s.sq_context_misc, XE_GPU_REG_SQ_CONTEXT_MISC); + dirty |= regs.vertex_shader != active_vertex_shader_; + dirty |= regs.pixel_shader != active_pixel_shader_; + dirty |= regs.prim_type != prim_type; + if (!dirty) { + return UpdateStatus::kCompatible; + } + regs.vertex_shader = static_cast(active_vertex_shader_); + regs.pixel_shader = static_cast(active_pixel_shader_); + regs.prim_type = prim_type; + + SCOPE_profile_cpu_f("gpu"); + + return UpdateStatus::kMismatch; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::UpdateRenderTargets() { + auto& regs = update_render_targets_regs_; + + bool dirty = false; + dirty |= SetShadowRegister(®s.rb_modecontrol, XE_GPU_REG_RB_MODECONTROL); + dirty |= SetShadowRegister(®s.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO); + dirty |= SetShadowRegister(®s.rb_color_info, XE_GPU_REG_RB_COLOR_INFO); + dirty |= SetShadowRegister(®s.rb_color1_info, XE_GPU_REG_RB_COLOR1_INFO); + dirty |= SetShadowRegister(®s.rb_color2_info, XE_GPU_REG_RB_COLOR2_INFO); + dirty |= SetShadowRegister(®s.rb_color3_info, XE_GPU_REG_RB_COLOR3_INFO); + dirty |= SetShadowRegister(®s.rb_color_mask, XE_GPU_REG_RB_COLOR_MASK); + dirty |= SetShadowRegister(®s.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL); + dirty |= + SetShadowRegister(®s.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK); + dirty |= SetShadowRegister(®s.rb_depth_info, XE_GPU_REG_RB_DEPTH_INFO); + if (!dirty) { + return UpdateStatus::kCompatible; + } + + SCOPE_profile_cpu_f("gpu"); + + return UpdateStatus::kMismatch; +} + +VulkanCommandProcessor::UpdateStatus VulkanCommandProcessor::UpdateState( + PrimitiveType prim_type) { + bool mismatch = false; + +#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \ + { \ + if (status == UpdateStatus::kError) { \ + XELOGE(error_message); \ + return status; \ + } else if (status == UpdateStatus::kMismatch) { \ + mismatch = true; \ + } \ + } + + UpdateStatus status; + status = UpdateViewportState(); + CHECK_UPDATE_STATUS(status, mismatch, "Unable to update viewport state"); + status = UpdateRasterizerState(prim_type); + CHECK_UPDATE_STATUS(status, mismatch, "Unable to update rasterizer state"); + status = UpdateBlendState(); + CHECK_UPDATE_STATUS(status, mismatch, "Unable to update blend state"); + status = UpdateDepthStencilState(); + CHECK_UPDATE_STATUS(status, mismatch, "Unable to update depth/stencil state"); + + return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::UpdateViewportState() { + auto& regs = update_viewport_state_regs_; + + bool dirty = false; + // dirty |= SetShadowRegister(&state_regs.pa_cl_clip_cntl, + // XE_GPU_REG_PA_CL_CLIP_CNTL); + dirty |= SetShadowRegister(®s.rb_surface_info, XE_GPU_REG_RB_SURFACE_INFO); + dirty |= SetShadowRegister(®s.pa_cl_vte_cntl, XE_GPU_REG_PA_CL_VTE_CNTL); + dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, + XE_GPU_REG_PA_SU_SC_MODE_CNTL); + dirty |= SetShadowRegister(®s.pa_sc_window_offset, + XE_GPU_REG_PA_SC_WINDOW_OFFSET); + dirty |= SetShadowRegister(®s.pa_sc_window_scissor_tl, + XE_GPU_REG_PA_SC_WINDOW_SCISSOR_TL); + dirty |= SetShadowRegister(®s.pa_sc_window_scissor_br, + XE_GPU_REG_PA_SC_WINDOW_SCISSOR_BR); + dirty |= SetShadowRegister(®s.pa_cl_vport_xoffset, + XE_GPU_REG_PA_CL_VPORT_XOFFSET); + dirty |= SetShadowRegister(®s.pa_cl_vport_yoffset, + XE_GPU_REG_PA_CL_VPORT_YOFFSET); + dirty |= SetShadowRegister(®s.pa_cl_vport_zoffset, + XE_GPU_REG_PA_CL_VPORT_ZOFFSET); + dirty |= SetShadowRegister(®s.pa_cl_vport_xscale, + XE_GPU_REG_PA_CL_VPORT_XSCALE); + dirty |= SetShadowRegister(®s.pa_cl_vport_yscale, + XE_GPU_REG_PA_CL_VPORT_YSCALE); + dirty |= SetShadowRegister(®s.pa_cl_vport_zscale, + XE_GPU_REG_PA_CL_VPORT_ZSCALE); + + // Much of this state machine is extracted from: + // https://github.com/freedreno/mesa/blob/master/src/mesa/drivers/dri/r200/r200_state.c + // http://fossies.org/dox/MesaLib-10.3.5/fd2__gmem_8c_source.html + // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf + + // http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf + // VTX_XY_FMT = true: the incoming X, Y have already been multiplied by 1/W0. + // = false: multiply the X, Y coordinates by 1/W0. + // VTX_Z_FMT = true: the incoming Z has already been multiplied by 1/W0. + // = false: multiply the Z coordinate by 1/W0. + // VTX_W0_FMT = true: the incoming W0 is not 1/W0. Perform the reciprocal to + // get 1/W0. + // draw_batcher_.set_vtx_fmt((regs.pa_cl_vte_cntl >> 8) & 0x1 ? 1.0f : 0.0f, + // (regs.pa_cl_vte_cntl >> 9) & 0x1 ? 1.0f : 0.0f, + // (regs.pa_cl_vte_cntl >> 10) & 0x1 ? 1.0f : 0.0f); + + // Done in VS, no need to flush state. + // if ((regs.pa_cl_vte_cntl & (1 << 0)) > 0) { + // draw_batcher_.set_window_scalar(1.0f, 1.0f); + //} else { + // draw_batcher_.set_window_scalar(1.0f / 2560.0f, -1.0f / 2560.0f); + //} + + if (!dirty) { + return UpdateStatus::kCompatible; + } + + return UpdateStatus::kMismatch; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::UpdateRasterizerState(PrimitiveType prim_type) { + auto& regs = update_rasterizer_state_regs_; + + bool dirty = false; + dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl, + XE_GPU_REG_PA_SU_SC_MODE_CNTL); + dirty |= SetShadowRegister(®s.pa_sc_screen_scissor_tl, + XE_GPU_REG_PA_SC_SCREEN_SCISSOR_TL); + dirty |= SetShadowRegister(®s.pa_sc_screen_scissor_br, + XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR); + dirty |= SetShadowRegister(®s.multi_prim_ib_reset_index, + XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX); + dirty |= regs.prim_type != prim_type; + if (!dirty) { + return UpdateStatus::kCompatible; + } + + regs.prim_type = prim_type; + + SCOPE_profile_cpu_f("gpu"); + + return UpdateStatus::kMismatch; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::UpdateBlendState() { + auto& reg_file = *register_file_; + auto& regs = update_blend_state_regs_; + + // Alpha testing -- ALPHAREF, ALPHAFUNC, ALPHATESTENABLE + // Deprecated in GL, implemented in shader. + // if(ALPHATESTENABLE && frag_out.a [<=/ALPHAFUNC] ALPHAREF) discard; + // uint32_t color_control = reg_file[XE_GPU_REG_RB_COLORCONTROL].u32; + // draw_batcher_.set_alpha_test((color_control & 0x4) != 0, // + // ALPAHTESTENABLE + // color_control & 0x7, // ALPHAFUNC + // reg_file[XE_GPU_REG_RB_ALPHA_REF].f32); + + bool dirty = false; + dirty |= + SetShadowRegister(®s.rb_blendcontrol[0], XE_GPU_REG_RB_BLENDCONTROL_0); + dirty |= + SetShadowRegister(®s.rb_blendcontrol[1], XE_GPU_REG_RB_BLENDCONTROL_1); + dirty |= + SetShadowRegister(®s.rb_blendcontrol[2], XE_GPU_REG_RB_BLENDCONTROL_2); + dirty |= + SetShadowRegister(®s.rb_blendcontrol[3], XE_GPU_REG_RB_BLENDCONTROL_3); + dirty |= SetShadowRegister(®s.rb_blend_rgba[0], XE_GPU_REG_RB_BLEND_RED); + dirty |= SetShadowRegister(®s.rb_blend_rgba[1], XE_GPU_REG_RB_BLEND_GREEN); + dirty |= SetShadowRegister(®s.rb_blend_rgba[2], XE_GPU_REG_RB_BLEND_BLUE); + dirty |= SetShadowRegister(®s.rb_blend_rgba[3], XE_GPU_REG_RB_BLEND_ALPHA); + if (!dirty) { + return UpdateStatus::kCompatible; + } + + SCOPE_profile_cpu_f("gpu"); + + return UpdateStatus::kMismatch; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::UpdateDepthStencilState() { + auto& regs = update_depth_stencil_state_regs_; + + bool dirty = false; + dirty |= SetShadowRegister(®s.rb_depthcontrol, XE_GPU_REG_RB_DEPTHCONTROL); + dirty |= + SetShadowRegister(®s.rb_stencilrefmask, XE_GPU_REG_RB_STENCILREFMASK); + if (!dirty) { + return UpdateStatus::kCompatible; + } + + SCOPE_profile_cpu_f("gpu"); + + return UpdateStatus::kMismatch; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::PopulateIndexBuffer( + IndexBufferInfo* index_buffer_info) { + auto& regs = *register_file_; + if (!index_buffer_info || !index_buffer_info->guest_base) { + // No index buffer or auto draw. + return UpdateStatus::kCompatible; + } + auto& info = *index_buffer_info; + +#if FINE_GRAINED_DRAW_SCOPES + SCOPE_profile_cpu_f("gpu"); +#endif // FINE_GRAINED_DRAW_SCOPES + + // Min/max index ranges for clamping. This is often [0g,FFFF|FFFFFF]. + // All indices should be clamped to [min,max]. May be a way to do this in GL. + uint32_t min_index = regs[XE_GPU_REG_VGT_MIN_VTX_INDX].u32; + uint32_t max_index = regs[XE_GPU_REG_VGT_MAX_VTX_INDX].u32; + assert_true(min_index == 0); + assert_true(max_index == 0xFFFF || max_index == 0xFFFFFF); + + assert_true(info.endianness == Endian::k8in16 || + info.endianness == Endian::k8in32); + + trace_writer_.WriteMemoryRead(info.guest_base, info.length); + + return UpdateStatus::kCompatible; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::PopulateVertexBuffers() { +#if FINE_GRAINED_DRAW_SCOPES + SCOPE_profile_cpu_f("gpu"); +#endif // FINE_GRAINED_DRAW_SCOPES + + auto& regs = *register_file_; + assert_not_null(active_vertex_shader_); + + for (const auto& vertex_binding : active_vertex_shader_->vertex_bindings()) { + int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + + (vertex_binding.fetch_constant / 3) * 6; + const auto group = reinterpret_cast(®s.values[r]); + const xe_gpu_vertex_fetch_t* fetch = nullptr; + switch (vertex_binding.fetch_constant % 3) { + case 0: + fetch = &group->vertex_fetch_0; + break; + case 1: + fetch = &group->vertex_fetch_1; + break; + case 2: + fetch = &group->vertex_fetch_2; + break; + } + assert_true(fetch->endian == 2); + + size_t valid_range = size_t(fetch->size * 4); + + trace_writer_.WriteMemoryRead(fetch->address << 2, valid_range); + } + + return UpdateStatus::kCompatible; +} + +VulkanCommandProcessor::UpdateStatus +VulkanCommandProcessor::PopulateSamplers() { +#if FINE_GRAINED_DRAW_SCOPES + SCOPE_profile_cpu_f("gpu"); +#endif // FINE_GRAINED_DRAW_SCOPES + + bool mismatch = false; + + // VS and PS samplers are shared, but may be used exclusively. + // We walk each and setup lazily. + bool has_setup_sampler[32] = {false}; + + // Vertex texture samplers. + for (auto& texture_binding : active_vertex_shader_->texture_bindings()) { + if (has_setup_sampler[texture_binding.fetch_constant]) { + continue; + } + has_setup_sampler[texture_binding.fetch_constant] = true; + auto status = PopulateSampler(texture_binding); + if (status == UpdateStatus::kError) { + return status; + } else if (status == UpdateStatus::kMismatch) { + mismatch = true; + } + } + + // Pixel shader texture sampler. + for (auto& texture_binding : active_pixel_shader_->texture_bindings()) { + if (has_setup_sampler[texture_binding.fetch_constant]) { + continue; + } + has_setup_sampler[texture_binding.fetch_constant] = true; + auto status = PopulateSampler(texture_binding); + if (status == UpdateStatus::kError) { + return UpdateStatus::kError; + } else if (status == UpdateStatus::kMismatch) { + mismatch = true; + } + } + + return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible; +} + +VulkanCommandProcessor::UpdateStatus VulkanCommandProcessor::PopulateSampler( + const Shader::TextureBinding& texture_binding) { + auto& regs = *register_file_; + int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + + texture_binding.fetch_constant * 6; + auto group = reinterpret_cast(®s.values[r]); + auto& fetch = group->texture_fetch; + + // ? + if (!fetch.type) { + return UpdateStatus::kCompatible; + } + assert_true(fetch.type == 0x2); + + TextureInfo texture_info; + if (!TextureInfo::Prepare(fetch, &texture_info)) { + XELOGE("Unable to parse texture fetcher info"); + return UpdateStatus::kCompatible; // invalid texture used + } + SamplerInfo sampler_info; + if (!SamplerInfo::Prepare(fetch, texture_binding.fetch_instr, + &sampler_info)) { + XELOGE("Unable to parse sampler info"); + return UpdateStatus::kCompatible; // invalid texture used + } + + trace_writer_.WriteMemoryRead(texture_info.guest_address, + texture_info.input_length); + + return UpdateStatus::kCompatible; +} + +bool VulkanCommandProcessor::IssueCopy() { + SCOPE_profile_cpu_f("gpu"); + return true; +} + +} // namespace vulkan +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h new file mode 100644 index 000000000..493345410 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -0,0 +1,165 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_VULKAN_COMMAND_PROCESSOR_H_ +#define XENIA_GPU_VULKAN_COMMAND_PROCESSOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenia/base/threading.h" +#include "xenia/gpu/command_processor.h" +#include "xenia/gpu/register_file.h" +#include "xenia/gpu/spirv_shader_translator.h" +#include "xenia/gpu/vulkan/vulkan_shader.h" +#include "xenia/gpu/xenos.h" +#include "xenia/kernel/xthread.h" +#include "xenia/memory.h" +#include "xenia/ui/vulkan/vulkan_context.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +class VulkanGraphicsSystem; + +class VulkanCommandProcessor : public CommandProcessor { + public: + VulkanCommandProcessor(VulkanGraphicsSystem* graphics_system, + kernel::KernelState* kernel_state); + ~VulkanCommandProcessor() override; + + void ClearCaches() override; + + private: + enum class UpdateStatus { + kCompatible, + kMismatch, + kError, + }; + + bool SetupContext() override; + void ShutdownContext() override; + + void MakeCoherent() override; + void PrepareForWait() override; + void ReturnFromWait() override; + + void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width, + uint32_t frontbuffer_height) override; + + Shader* LoadShader(ShaderType shader_type, uint32_t guest_address, + const uint32_t* host_address, + uint32_t dword_count) override; + + bool IssueDraw(PrimitiveType prim_type, uint32_t index_count, + IndexBufferInfo* index_buffer_info) override; + UpdateStatus UpdateShaders(PrimitiveType prim_type); + UpdateStatus UpdateRenderTargets(); + UpdateStatus UpdateState(PrimitiveType prim_type); + UpdateStatus UpdateViewportState(); + UpdateStatus UpdateRasterizerState(PrimitiveType prim_type); + UpdateStatus UpdateBlendState(); + UpdateStatus UpdateDepthStencilState(); + UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info); + UpdateStatus PopulateVertexBuffers(); + UpdateStatus PopulateSamplers(); + UpdateStatus PopulateSampler(const Shader::TextureBinding& texture_binding); + bool IssueCopy() override; + + SpirvShaderTranslator shader_translator_; + + private: + bool SetShadowRegister(uint32_t* dest, uint32_t register_name); + bool SetShadowRegister(float* dest, uint32_t register_name); + struct UpdateRenderTargetsRegisters { + uint32_t rb_modecontrol; + uint32_t rb_surface_info; + uint32_t rb_color_info; + uint32_t rb_color1_info; + uint32_t rb_color2_info; + uint32_t rb_color3_info; + uint32_t rb_color_mask; + uint32_t rb_depthcontrol; + uint32_t rb_stencilrefmask; + uint32_t rb_depth_info; + + UpdateRenderTargetsRegisters() { Reset(); } + void Reset() { std::memset(this, 0, sizeof(*this)); } + } update_render_targets_regs_; + struct UpdateViewportStateRegisters { + // uint32_t pa_cl_clip_cntl; + uint32_t rb_surface_info; + uint32_t pa_cl_vte_cntl; + uint32_t pa_su_sc_mode_cntl; + uint32_t pa_sc_window_offset; + uint32_t pa_sc_window_scissor_tl; + uint32_t pa_sc_window_scissor_br; + float pa_cl_vport_xoffset; + float pa_cl_vport_yoffset; + float pa_cl_vport_zoffset; + float pa_cl_vport_xscale; + float pa_cl_vport_yscale; + float pa_cl_vport_zscale; + + UpdateViewportStateRegisters() { Reset(); } + void Reset() { std::memset(this, 0, sizeof(*this)); } + } update_viewport_state_regs_; + struct UpdateRasterizerStateRegisters { + uint32_t pa_su_sc_mode_cntl; + uint32_t pa_sc_screen_scissor_tl; + uint32_t pa_sc_screen_scissor_br; + uint32_t multi_prim_ib_reset_index; + PrimitiveType prim_type; + + UpdateRasterizerStateRegisters() { Reset(); } + void Reset() { std::memset(this, 0, sizeof(*this)); } + } update_rasterizer_state_regs_; + struct UpdateBlendStateRegisters { + uint32_t rb_blendcontrol[4]; + float rb_blend_rgba[4]; + + UpdateBlendStateRegisters() { Reset(); } + void Reset() { std::memset(this, 0, sizeof(*this)); } + } update_blend_state_regs_; + struct UpdateDepthStencilStateRegisters { + uint32_t rb_depthcontrol; + uint32_t rb_stencilrefmask; + + UpdateDepthStencilStateRegisters() { Reset(); } + void Reset() { std::memset(this, 0, sizeof(*this)); } + } update_depth_stencil_state_regs_; + struct UpdateShadersRegisters { + PrimitiveType prim_type; + uint32_t pa_su_sc_mode_cntl; + uint32_t sq_program_cntl; + uint32_t sq_context_misc; + VulkanShader* vertex_shader; + VulkanShader* pixel_shader; + + UpdateShadersRegisters() { Reset(); } + void Reset() { + sq_program_cntl = 0; + vertex_shader = pixel_shader = nullptr; + } + } update_shaders_regs_; +}; + +} // namespace vulkan +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_VULKAN_COMMAND_PROCESSOR_H_ diff --git a/src/xenia/gpu/vulkan/vulkan_gpu_flags.cc b/src/xenia/gpu/vulkan/vulkan_gpu_flags.cc new file mode 100644 index 000000000..675e60476 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_gpu_flags.cc @@ -0,0 +1,10 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/vulkan/vulkan_gpu_flags.h" diff --git a/src/xenia/gpu/vulkan/vulkan_gpu_flags.h b/src/xenia/gpu/vulkan/vulkan_gpu_flags.h new file mode 100644 index 000000000..c78637a47 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_gpu_flags.h @@ -0,0 +1,15 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_VULKAN_VULKAN_GPU_FLAGS_H_ +#define XENIA_GPU_VULKAN_VULKAN_GPU_FLAGS_H_ + +#include + +#endif // XENIA_GPU_VULKAN_VULKAN_GPU_FLAGS_H_ diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc new file mode 100644 index 000000000..74ec57849 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc @@ -0,0 +1,87 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/vulkan/vulkan_graphics_system.h" + +#include +#include + +#include "xenia/base/logging.h" +#include "xenia/base/profiling.h" +#include "xenia/cpu/processor.h" +#include "xenia/gpu/gpu_flags.h" +#include "xenia/gpu/vulkan/vulkan_command_processor.h" +#include "xenia/gpu/vulkan/vulkan_gpu_flags.h" +#include "xenia/ui/vulkan/vulkan_provider.h" +#include "xenia/ui/window.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +VulkanGraphicsSystem::VulkanGraphicsSystem() = default; + +VulkanGraphicsSystem::~VulkanGraphicsSystem() = default; + +X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor, + kernel::KernelState* kernel_state, + ui::Window* target_window) { + // Must create the provider so we can create contexts. + provider_ = xe::ui::vulkan::VulkanProvider::Create(target_window); + + auto result = GraphicsSystem::Setup(processor, kernel_state, target_window); + if (result) { + return result; + } + + display_context_ = reinterpret_cast( + target_window->context()); + + return X_STATUS_SUCCESS; +} + +void VulkanGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); } + +std::unique_ptr +VulkanGraphicsSystem::CreateCommandProcessor() { + return std::unique_ptr( + new VulkanCommandProcessor(this, kernel_state_)); +} + +void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { + if (!command_processor_) { + return; + } + // Check for pending swap. + auto& swap_state = command_processor_->swap_state(); + { + std::lock_guard lock(swap_state.mutex); + if (swap_state.pending) { + swap_state.pending = false; + std::swap(swap_state.front_buffer_texture, + swap_state.back_buffer_texture); + } + } + + if (!swap_state.front_buffer_texture) { + // Not yet ready. + return; + } + + // Blit the frontbuffer. + // display_context_->blitter()->BlitTexture2D( + // static_cast(swap_state.front_buffer_texture), + // Rect2D(0, 0, swap_state.width, swap_state.height), + // Rect2D(0, 0, target_window_->width(), target_window_->height()), + // GL_LINEAR, false); +} + +} // namespace vulkan +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.h b/src/xenia/gpu/vulkan/vulkan_graphics_system.h new file mode 100644 index 000000000..e486452aa --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.h @@ -0,0 +1,43 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_VULKAN_VULKAN_GRAPHICS_SYSTEM_H_ +#define XENIA_GPU_VULKAN_VULKAN_GRAPHICS_SYSTEM_H_ + +#include + +#include "xenia/gpu/graphics_system.h" +#include "xenia/ui/vulkan/vulkan_context.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +class VulkanGraphicsSystem : public GraphicsSystem { + public: + VulkanGraphicsSystem(); + ~VulkanGraphicsSystem() override; + + X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, + ui::Window* target_window) override; + void Shutdown() override; + + private: + std::unique_ptr CreateCommandProcessor() override; + + void Swap(xe::ui::UIEvent* e) override; + + xe::ui::vulkan::VulkanContext* display_context_ = nullptr; +}; + +} // namespace vulkan +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_VULKAN_VULKAN_GRAPHICS_SYSTEM_H_ diff --git a/src/xenia/gpu/vulkan/vulkan_shader.cc b/src/xenia/gpu/vulkan/vulkan_shader.cc new file mode 100644 index 000000000..00b68af42 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_shader.cc @@ -0,0 +1,27 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/vulkan/vulkan_shader.h" + +#include "xenia/base/logging.h" +#include "xenia/base/math.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +VulkanShader::VulkanShader(ShaderType shader_type, uint64_t data_hash, + const uint32_t* dword_ptr, uint32_t dword_count) + : Shader(shader_type, data_hash, dword_ptr, dword_count) {} + +VulkanShader::~VulkanShader() = default; + +} // namespace vulkan +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/vulkan/vulkan_shader.h b/src/xenia/gpu/vulkan/vulkan_shader.h new file mode 100644 index 000000000..9277ae44f --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_shader.h @@ -0,0 +1,33 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_VULKAN_VULKAN_SHADER_H_ +#define XENIA_GPU_VULKAN_VULKAN_SHADER_H_ + +#include + +#include "xenia/gpu/shader.h" +#include "xenia/ui/vulkan/vulkan_context.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +class VulkanShader : public Shader { + public: + VulkanShader(ShaderType shader_type, uint64_t data_hash, + const uint32_t* dword_ptr, uint32_t dword_count); + ~VulkanShader() override; +}; + +} // namespace vulkan +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_VULKAN_VULKAN_SHADER_H_ diff --git a/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc b/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc new file mode 100644 index 000000000..6099a7cf1 --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_trace_dump_main.cc @@ -0,0 +1,76 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/base/main.h" +#include "xenia/gpu/trace_dump.h" +#include "xenia/gpu/vulkan/vulkan_command_processor.h" +#include "xenia/gpu/vulkan/vulkan_graphics_system.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +using namespace xe::gpu::xenos; + +class VulkanTraceDump : public TraceDump { + public: + std::unique_ptr CreateGraphicsSystem() override { + return std::unique_ptr(new VulkanGraphicsSystem()); + } + + uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples, + uint32_t base, + ColorRenderTargetFormat format) override { + auto command_processor = static_cast( + graphics_system_->command_processor()); + // return command_processor->GetColorRenderTarget(pitch, samples, base, + // format); + return 0; + } + + uintptr_t GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples, + uint32_t base, + DepthRenderTargetFormat format) override { + auto command_processor = static_cast( + graphics_system_->command_processor()); + // return command_processor->GetDepthRenderTarget(pitch, samples, base, + // format); + return 0; + } + + uintptr_t GetTextureEntry(const TextureInfo& texture_info, + const SamplerInfo& sampler_info) override { + auto command_processor = static_cast( + graphics_system_->command_processor()); + + // auto entry_view = + // command_processor->texture_cache()->Demand(texture_info, + // sampler_info); + // if (!entry_view) { + // return 0; + //} + // auto texture = entry_view->texture; + // return static_cast(texture->handle); + return 0; + } +}; + +int trace_dump_main(const std::vector& args) { + VulkanTraceDump trace_dump; + return trace_dump.Main(args); +} + +} // namespace vulkan +} // namespace gpu +} // namespace xe + +DEFINE_ENTRY_POINT(L"xenia-gpu-vulkan-trace-dump", + L"xenia-gpu-vulkan-trace-dump some.trace", + xe::gpu::vulkan::trace_dump_main); diff --git a/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc b/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc new file mode 100644 index 000000000..b2cc8c30a --- /dev/null +++ b/src/xenia/gpu/vulkan/vulkan_trace_viewer_main.cc @@ -0,0 +1,76 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/base/main.h" +#include "xenia/gpu/trace_viewer.h" +#include "xenia/gpu/vulkan/vulkan_command_processor.h" +#include "xenia/gpu/vulkan/vulkan_graphics_system.h" + +namespace xe { +namespace gpu { +namespace vulkan { + +using namespace xe::gpu::xenos; + +class VulkanTraceViewer : public TraceViewer { + public: + std::unique_ptr CreateGraphicsSystem() override { + return std::unique_ptr(new VulkanGraphicsSystem()); + } + + uintptr_t GetColorRenderTarget(uint32_t pitch, MsaaSamples samples, + uint32_t base, + ColorRenderTargetFormat format) override { + auto command_processor = static_cast( + graphics_system_->command_processor()); + // return command_processor->GetColorRenderTarget(pitch, samples, base, + // format); + return 0; + } + + uintptr_t GetDepthRenderTarget(uint32_t pitch, MsaaSamples samples, + uint32_t base, + DepthRenderTargetFormat format) override { + auto command_processor = static_cast( + graphics_system_->command_processor()); + // return command_processor->GetDepthRenderTarget(pitch, samples, base, + // format); + return 0; + } + + uintptr_t GetTextureEntry(const TextureInfo& texture_info, + const SamplerInfo& sampler_info) override { + auto command_processor = static_cast( + graphics_system_->command_processor()); + + // auto entry_view = + // command_processor->texture_cache()->Demand(texture_info, + // sampler_info); + // if (!entry_view) { + // return 0; + //} + // auto texture = entry_view->texture; + // return static_cast(texture->handle); + return 0; + } +}; + +int trace_viewer_main(const std::vector& args) { + VulkanTraceViewer trace_viewer; + return trace_viewer.Main(args); +} + +} // namespace vulkan +} // namespace gpu +} // namespace xe + +DEFINE_ENTRY_POINT(L"xenia-gpu-vulkan-trace-viewer", + L"xenia-gpu-vulkan-trace-viewer some.trace", + xe::gpu::vulkan::trace_viewer_main); diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc index ae44cab00..463e7ece0 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc @@ -686,13 +686,13 @@ void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) { } // Setup texture binding. - VkDescriptorSet texture_set = nullptr; auto texture = reinterpret_cast(draw.texture_handle); if (texture) { - texture_set = texture->descriptor_set(); + auto texture_set = texture->descriptor_set(); + vkCmdBindDescriptorSets(current_cmd_buffer_, + VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_, + 0, 1, &texture_set, 0, nullptr); } - vkCmdBindDescriptorSets(current_cmd_buffer_, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipeline_layout_, 0, 1, &texture_set, 0, nullptr); // Use push constants for our per-draw changes. // Here, the restrict_texture_samples uniform.