diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp index 75dcbf571..16c60d17e 100644 --- a/src/video_core/rasterizer_accelerated.cpp +++ b/src/video_core/rasterizer_accelerated.cpp @@ -766,8 +766,9 @@ void RasterizerAccelerated::SyncProcTexNoise() { void RasterizerAccelerated::SyncProcTexBias() { const auto& regs = Pica::g_state.regs.texturing; - const auto proctex_bias = Pica::float16::FromRaw(regs.proctex.bias_low | - (regs.proctex_lut.bias_high << 8)).ToFloat32(); + const auto proctex_bias = + Pica::float16::FromRaw(regs.proctex.bias_low | (regs.proctex_lut.bias_high << 8)) + .ToFloat32(); if (proctex_bias != uniform_block_data.data.proctex_bias) { uniform_block_data.data.proctex_bias = proctex_bias; uniform_block_data.dirty = true; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8b3e6087a..df37cb89c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1372,9 +1372,11 @@ void RasterizerOpenGL::UploadUniforms(bool accelerate_draw) { } if (sync_fs || invalidate) { - std::memcpy(uniforms + used_bytes, &uniform_block_data.data, sizeof(Pica::Shader::UniformData)); - glBindBufferRange(GL_UNIFORM_BUFFER, static_cast(Pica::Shader::UniformBindings::Common), - uniform_buffer.GetHandle(), offset + used_bytes, sizeof(Pica::Shader::UniformData)); + std::memcpy(uniforms + used_bytes, &uniform_block_data.data, + sizeof(Pica::Shader::UniformData)); + glBindBufferRange( + GL_UNIFORM_BUFFER, static_cast(Pica::Shader::UniformBindings::Common), + uniform_buffer.GetHandle(), offset + used_bytes, sizeof(Pica::Shader::UniformData)); uniform_block_data.dirty = false; used_bytes += uniform_size_aligned_fs; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 645d6893e..fc71eb85b 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -334,7 +334,8 @@ private: case RegisterType::FloatUniform: if (address_register_index != 0) { // TODO: Verify hardware behavior of out-of-bounds register number. - return fmt::format("({0} + address_registers.{1} < 96 ? uniforms.f[{0} + address_registers.{1}] : vec4(0))", + return fmt::format("({0} + address_registers.{1} < 96 ? uniforms.f[{0} + " + "address_registers.{1}] : vec4(0))", index, "xyz"[address_register_index - 1]); } return fmt::format("uniforms.f[{}]", index); diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 69ecdb262..bd626206d 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -85,7 +85,8 @@ static std::tuple BuildVSConfigFromRaw( return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup}; } -static void SetShaderUniformBlockBinding(GLuint shader, const char* name, Pica::Shader::UniformBindings binding, +static void SetShaderUniformBlockBinding(GLuint shader, const char* name, + Pica::Shader::UniformBindings binding, std::size_t expected_size) { const GLuint ub_index = glGetUniformBlockIndex(shader, name); if (ub_index == GL_INVALID_INDEX) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d9851a02d..4fbd18777 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -298,7 +298,7 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) { handle = fragment_shaders_spv.Get(config, instance.GetDevice()); } else { handle = fragment_shaders_glsl.Get(config, vk::ShaderStageFlagBits::eFragment, - instance.GetDevice(), ShaderOptimization::High); + instance.GetDevice(), ShaderOptimization::Debug); } current_shaders[ProgramType::FS] = handle; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 71d5eea30..556209b85 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1201,8 +1201,8 @@ void RasterizerVulkan::SyncLogicOp() { void RasterizerVulkan::SyncColorWriteMask() { const auto& regs = Pica::g_state.regs; const u32 color_mask = regs.framebuffer.framebuffer.allow_color_write != 0 - ? (regs.framebuffer.output_merger.depth_color_mask >> 8) & 0xF - : 0; + ? (regs.framebuffer.output_merger.depth_color_mask >> 8) & 0xF + : 0; const bool is_logic_op_emulated = instance.NeedsLogicOpEmulation() && !regs.framebuffer.output_merger.alphablend_enable; diff --git a/src/video_core/renderer_vulkan/vk_shader_gen.cpp b/src/video_core/renderer_vulkan/vk_shader_gen.cpp index 501d3b947..221e5e97e 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen.cpp @@ -1587,12 +1587,12 @@ void main() { gl_Position = vert_position; gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0; - //gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 - //if (enable_clip1) { - // gl_ClipDistance[1] = dot(clip_coef, vert_position); - //} else { - // gl_ClipDistance[1] = 0; - //} + gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 + if (enable_clip1) { + gl_ClipDistance[1] = dot(clip_coef, vert_position); + } else { + gl_ClipDistance[1] = 0; + } } )"; diff --git a/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp b/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp index 869a3edce..a3cc8746c 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp @@ -42,6 +42,9 @@ void FragmentModule::Generate() { return; } + // Check if the fragment is outside scissor rectangle + WriteScissor(); + // Write shader bytecode to emulate all enabled PICA lights if (config.state.lighting.enable) { WriteLighting(); @@ -56,9 +59,7 @@ void FragmentModule::Generate() { WriteTevStage(static_cast(index)); } - if (WriteAlphaTestCondition(config.state.alpha_test_func)) { - return; - } + WriteAlphaTestCondition(config.state.alpha_test_func); if (config.state.fog_mode == TexturingRegs::FogMode::Gas) { Core::System::GetInstance().TelemetrySession().AddField( @@ -124,6 +125,45 @@ void FragmentModule::WriteDepth() { } } +void FragmentModule::WriteScissor() { + if (config.state.scissor_test_mode == RasterizerRegs::ScissorMode::Disabled) { + return; + } + + const Id input_pointer_id{TypePointer(spv::StorageClass::Input, vec_ids.Get(4))}; + const Id input_pointer{OpAccessChain(input_pointer_id, gl_frag_coord_id)}; + const Id gl_frag_coord{OpLoad(vec_ids.Get(4), input_pointer)}; + const Id gl_frag_coord_xy{OpVectorShuffle(vec_ids.Get(2), gl_frag_coord, gl_frag_coord, 0, 1)}; + + const Id scissor_x1{GetShaderDataMember(i32_id, ConstS32(6))}; + const Id scissor_y1{GetShaderDataMember(i32_id, ConstS32(7))}; + const Id scissor_1{OpCompositeConstruct(vec_ids.Get(2), OpConvertSToF(f32_id, scissor_x1), + OpConvertSToF(f32_id, scissor_y1))}; + + const Id scissor_x2{GetShaderDataMember(i32_id, ConstS32(8))}; + const Id scissor_y2{GetShaderDataMember(i32_id, ConstS32(9))}; + const Id scissor_2{OpCompositeConstruct(vec_ids.Get(2), OpConvertSToF(f32_id, scissor_x2), + OpConvertSToF(f32_id, scissor_y2))}; + + const Id cond1{OpFOrdGreaterThanEqual(bvec_ids.Get(2), gl_frag_coord_xy, scissor_1)}; + const Id cond2{OpFOrdLessThan(bvec_ids.Get(2), gl_frag_coord_xy, scissor_2)}; + + Id result{OpAll(bool_id, OpCompositeConstruct(bvec_ids.Get(4), cond1, cond2))}; + if (config.state.scissor_test_mode == RasterizerRegs::ScissorMode::Include) { + result = OpLogicalNot(bool_id, result); + } + + const Id merge_block{OpLabel()}; + const Id kill_label{OpLabel()}; + OpSelectionMerge(merge_block, spv::SelectionControlMask::MaskNone); + OpBranchConditional(result, kill_label, merge_block); + + AddLabel(kill_label); + OpKill(); + + AddLabel(merge_block); +} + void FragmentModule::WriteLighting() { const auto& lighting = config.state.lighting; @@ -570,7 +610,7 @@ using ProcTexShift = TexturingRegs::ProcTexShift; using ProcTexCombiner = TexturingRegs::ProcTexCombiner; using ProcTexFilter = TexturingRegs::ProcTexFilter; -bool FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) { +void FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) { using CompareFunc = FramebufferRegs::CompareFunc; // The compare func is to keep the fragment so we invert it to discard it @@ -593,13 +633,10 @@ bool FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) } }; + // Don't check for kill, this is done earlier switch (func) { - case CompareFunc::Never: // Kill the fragment - OpKill(); - OpFunctionEnd(); - return true; case CompareFunc::Always: // Do nothing - return false; + break; case CompareFunc::Equal: case CompareFunc::NotEqual: case CompareFunc::LessThan: @@ -618,10 +655,9 @@ bool FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) AddLabel(kill_label); OpKill(); AddLabel(keep_label); - return false; + break; } default: - return false; LOG_CRITICAL(Render_Vulkan, "Unknown alpha test condition {}", func); break; } @@ -975,9 +1011,7 @@ void FragmentModule::DefineProcTexSampler() { const Id proctex_alpha_map_offset{GetShaderDataMember(i32_id, ConstS32(13))}; const Id final_alpha{AppendProcTexCombineAndMap(config.state.proctex.alpha_combiner, u, v, proctex_alpha_map_offset)}; - const Id final_color_xyz{ - OpVectorShuffle(vec_ids.Get(3), final_color, final_color, 0, 1, 2)}; - final_color = OpCompositeConstruct(vec_ids.Get(4), final_color_xyz, final_alpha); + final_color = OpCompositeInsert(vec_ids.Get(4), final_alpha, final_color, 3); } OpReturnValue(final_color); diff --git a/src/video_core/renderer_vulkan/vk_shader_gen_spv.h b/src/video_core/renderer_vulkan/vk_shader_gen_spv.h index e66d87f85..0d6f57c8e 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen_spv.h +++ b/src/video_core/renderer_vulkan/vk_shader_gen_spv.h @@ -33,9 +33,13 @@ public: /// Emits SPIR-V bytecode corresponding to the provided pica fragment configuration void Generate(); - /// Undos the vulkan perspective transformation and applies the pica one +private: + /// Undos the vulkan perspective transformation and applies the PICA one void WriteDepth(); + /// Emits code to emulate the scissor rectangle + void WriteScissor(); + /// Writes the code to emulate fragment lighting void WriteLighting(); @@ -46,8 +50,7 @@ public: void DefineProcTexSampler(); /// Writes the if-statement condition used to evaluate alpha testing. - /// Returns true if the fragment was discarded - [[nodiscard]] bool WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func); + void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func); /// Samples the current fragment texel from the provided texture unit [[nodiscard]] Id SampleTexture(u32 texture_unit);