diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp index 37679b6a6..904be2ea0 100644 --- a/src/video_core/rasterizer_accelerated.cpp +++ b/src/video_core/rasterizer_accelerated.cpp @@ -643,6 +643,18 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { uniform_block_data.lighting_lut_dirty_any = true; break; } + + // Texture LOD biases + case PICA_REG_INDEX(texturing.texture0.lod.bias): + SyncTextureLodBias(0); + break; + case PICA_REG_INDEX(texturing.texture1.lod.bias): + SyncTextureLodBias(1); + break; + case PICA_REG_INDEX(texturing.texture2.lod.bias): + SyncTextureLodBias(2); + break; + default: // Forward registers that map to fixed function API features to the video backend NotifyFixedFunctionPicaRegisterChanged(id); @@ -840,4 +852,13 @@ void RasterizerAccelerated::SyncShadowTextureBias() { } } +void RasterizerAccelerated::SyncTextureLodBias(int tex_index) { + const auto pica_textures = Pica::g_state.regs.texturing.GetTextures(); + const float bias = pica_textures[tex_index].config.lod.bias / 256.0f; + if (bias != uniform_block_data.data.tex_lod_bias[tex_index]) { + uniform_block_data.data.tex_lod_bias[tex_index] = bias; + uniform_block_data.dirty = true; + } +} + } // namespace VideoCore diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h index e2270fd2d..cf295134b 100644 --- a/src/video_core/rasterizer_accelerated.h +++ b/src/video_core/rasterizer_accelerated.h @@ -85,6 +85,9 @@ protected: /// Syncs the shadow texture bias to match the PICA register void SyncShadowTextureBias(); + /// Syncs the texture LOD bias to match the PICA register + void SyncTextureLodBias(int tex_index); + protected: /// Structure that keeps tracks of the uniform state struct UniformBlockData { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c872912c2..eac829cbb 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -176,6 +176,10 @@ void RasterizerOpenGL::SyncEntireState() { SyncProcTexBias(); SyncShadowBias(); SyncShadowTextureBias(); + + for (unsigned tex_index = 0; tex_index < 3; tex_index++) { + SyncTextureLodBias(tex_index); + } } static constexpr std::array vs_attrib_types{ @@ -991,7 +995,6 @@ void RasterizerOpenGL::SamplerInfo::Create() { wrap_s = wrap_t = TextureConfig::Repeat; border_color = 0; lod_min = lod_max = 0; - lod_bias = 0; // default is 1000 and -1000 // Other attributes have correct defaults @@ -1053,11 +1056,6 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig( lod_max = config.lod.max_level; glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, static_cast(lod_max)); } - - if (!GLES && lod_bias != config.lod.bias) { - lod_bias = config.lod.bias; - glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias / 256.0f); - } } void RasterizerOpenGL::SetShader() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 1383ffedb..b32a64911 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -66,7 +66,6 @@ private: u32 border_color; u32 lod_min; u32 lod_max; - s32 lod_bias; // TODO(wwylele): remove this once mipmap for cube is implemented bool supress_mipmap_for_cube = false; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index f1b2ef713..e05c03749 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -68,6 +68,7 @@ layout (std140) uniform shader_data { vec4 const_color[NUM_TEV_STAGES]; vec4 tev_combiner_buffer_color; vec4 clip_coef; + vec3 tex_lod_bias; }; )"; @@ -307,7 +308,7 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un // Only unit 0 respects the texturing type switch (state.texture0_type) { case TexturingRegs::TextureConfig::Texture2D: - return "textureLod(tex0, texcoord0, getLod(texcoord0 * vec2(textureSize(tex0, 0))))"; + return "textureLod(tex0, texcoord0, getLod(texcoord0 * vec2(textureSize(tex0, 0))) + tex_lod_bias[0])"; case TexturingRegs::TextureConfig::Projection2D: // TODO (wwylele): find the exact LOD formula for projection texture return "textureProj(tex0, vec3(texcoord0, texcoord0_w))"; @@ -325,12 +326,12 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un return "texture(tex0, texcoord0)"; } case 1: - return "textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, 0))))"; + return "textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, 0))) + tex_lod_bias[1])"; case 2: if (state.texture2_use_coord1) - return "textureLod(tex2, texcoord1, getLod(texcoord1 * vec2(textureSize(tex2, 0))))"; + return "textureLod(tex2, texcoord1, getLod(texcoord1 * vec2(textureSize(tex2, 0))) + tex_lod_bias[2])"; else - return "textureLod(tex2, texcoord2, getLod(texcoord2 * vec2(textureSize(tex2, 0))))"; + return "textureLod(tex2, texcoord2, getLod(texcoord2 * vec2(textureSize(tex2, 0))) + tex_lod_bias[2])"; case 3: if (state.proctex.enable) { return "ProcTex()"; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 24be013fe..40887ab1a 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -356,6 +356,7 @@ void Instance::CreateFormatTable() { bool Instance::CreateDevice() { const vk::StructureChain feature_chain = physical_device.getFeatures2(), feature_chain.get(), feature_chain.get(), feature_chain.get(), feature_chain.get(), }; + if (portability_subset) { + const vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features = + feature_chain.get(); + triangle_fan_supported = portability_features.triangleFans; + + const vk::StructureChain portability_properties_chain = + physical_device.getProperties2(); + const vk::PhysicalDevicePortabilitySubsetPropertiesKHR portability_properties = + portability_properties_chain.get(); + min_vertex_stride_alignment = portability_properties.minVertexInputBindingStrideAlignment; + } else { + device_chain.unlink(); + } + if (!index_type_uint8) { device_chain.unlink(); } @@ -491,22 +510,6 @@ bool Instance::CreateDevice() { device_chain.unlink(); } -#if __APPLE__ - const vk::StructureChain portability_features_chain = - physical_device.getFeatures2(); - const vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features = - portability_features_chain.get(); - triangle_fan_supported = portability_features.triangleFans; - - const vk::StructureChain portability_properties_chain = - physical_device.getProperties2(); - const vk::PhysicalDevicePortabilitySubsetPropertiesKHR portability_properties = - portability_properties_chain.get(); - min_vertex_stride_alignment = portability_properties.minVertexInputBindingStrideAlignment; -#endif - try { device = physical_device.createDevice(device_chain.get()); } catch (vk::ExtensionNotPresentError& err) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 1565aa9f7..6ea217f04 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -184,6 +184,10 @@ void RasterizerVulkan::SyncEntireState() { SyncProcTexBias(); SyncShadowBias(); SyncShadowTextureBias(); + + for (unsigned tex_index = 0; tex_index < 3; tex_index++) { + SyncTextureLodBias(tex_index); + } } void RasterizerVulkan::SyncFixedState() { @@ -593,8 +597,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { .wrap_t = config.wrap_t, .border_color = config.border_color.raw, .lod_min = skip_mipmap ? 0.f : static_cast(config.lod.min_level), - .lod_max = skip_mipmap ? 0.f : static_cast(config.lod.max_level), - .lod_bias = static_cast(config.lod.bias), + .lod_max = skip_mipmap ? 0.f : static_cast(config.lod.max_level) }; // Search the cache and bind the appropriate sampler @@ -1141,7 +1144,7 @@ vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& info) { .mipmapMode = PicaToVK::TextureMipFilterMode(info.mip_filter), .addressModeU = PicaToVK::WrapMode(info.wrap_s), .addressModeV = PicaToVK::WrapMode(info.wrap_t), - .mipLodBias = info.lod_bias / 256.0f, + .mipLodBias = 0, .anisotropyEnable = instance.IsAnisotropicFilteringSupported(), .maxAnisotropy = properties.limits.maxSamplerAnisotropy, .compareEnable = false, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 71b0f50f4..f12c372c8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -34,7 +34,6 @@ struct SamplerInfo { u32 border_color = 0; float lod_min = 0; float lod_max = 0; - float lod_bias = 0; // TODO(wwylele): remove this once mipmap for cube is implemented bool supress_mipmap_for_cube = false; diff --git a/src/video_core/renderer_vulkan/vk_shader_gen.cpp b/src/video_core/renderer_vulkan/vk_shader_gen.cpp index 86da7715b..6e3c28d54 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen.cpp @@ -69,6 +69,7 @@ layout (set = 0, binding = 1, std140) uniform shader_data { vec4 const_color[NUM_TEV_STAGES]; vec4 tev_combiner_buffer_color; vec4 clip_coef; + vec3 tex_lod_bias; }; )"; @@ -325,7 +326,7 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un switch (state.texture0_type) { case TexturingRegs::TextureConfig::Texture2D: return "textureLod(sampler2D(tex0, tex0_sampler), texcoord0, getLod(texcoord0 * " - "vec2(textureSize(sampler2D(tex0, tex0_sampler), 0))))"; + "vec2(textureSize(sampler2D(tex0, tex0_sampler), 0))) + tex_lod_bias[0])"; case TexturingRegs::TextureConfig::Projection2D: // TODO (wwylele): find the exact LOD formula for projection texture return "textureProj(sampler2D(tex0, tex0_sampler), vec3(texcoord0, texcoord0_w))"; @@ -344,14 +345,14 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un } case 1: return "textureLod(sampler2D(tex1, tex1_sampler), texcoord1, getLod(texcoord1 * " - "vec2(textureSize(sampler2D(tex1, tex1_sampler), 0))))"; + "vec2(textureSize(sampler2D(tex1, tex1_sampler), 0))) + tex_lod_bias[1])"; case 2: if (state.texture2_use_coord1) return "textureLod(sampler2D(tex2, tex2_sampler), texcoord1, getLod(texcoord1 * " - "vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))"; + "vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))) + tex_lod_bias[2])"; else return "textureLod(sampler2D(tex2, tex2_sampler), texcoord2, getLod(texcoord2 * " - "vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))))"; + "vec2(textureSize(sampler2D(tex2, tex2_sampler), 0))) + tex_lod_bias[2])"; case 3: if (state.proctex.enable) { return "ProcTex()"; 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 715dcf91f..eb65bde64 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp @@ -610,7 +610,7 @@ Id FragmentModule::SampleTexture(u32 texture_unit) { // This LOD formula is the same as the LOD lower limit defined in OpenGL. // f(x, y) >= max{m_u, m_v, m_w} // (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail) - const auto SampleLod = [this](Id tex_id, Id tex_sampler_id, Id texcoord_id) { + const auto SampleLod = [this, texture_unit](Id tex_id, Id tex_sampler_id, Id texcoord_id) { const Id tex{OpLoad(image2d_id, tex_id)}; const Id tex_sampler{OpLoad(sampler_id, tex_sampler_id)}; const Id sampled_image{OpSampledImage(TypeSampledImage(image2d_id), tex, tex_sampler)}; @@ -624,8 +624,10 @@ Id FragmentModule::SampleTexture(u32 texture_unit) { const Id dx_dy_max{ OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))}; const Id lod{OpLog2(f32_id, dx_dy_max)}; + const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(29), ConstU32(texture_unit))}; + const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)}; return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord, - spv::ImageOperandsMask::Lod, lod); + spv::ImageOperandsMask::Lod, biased_lod); }; const auto Sample = [this](Id tex_id, Id tex_sampler_id, bool projection) { @@ -1342,12 +1344,12 @@ void FragmentModule::DefineUniformStructs() { i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id, lighting_lut_array_id, vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(3), - light_src_array_id, const_color_array_id, vec_ids.Get(4), vec_ids.Get(4))}; + light_src_array_id, const_color_array_id, vec_ids.Get(4), vec_ids.Get(4), vec_ids.Get(3))}; constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u}; constexpr std::array shader_data_offsets{ 0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u, 32u, 36u, 40u, 44u, 48u, 52u, 56u, - 60u, 64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u}; + 60u, 64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u, 1264u}; Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u); Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u); diff --git a/src/video_core/shader/shader_uniforms.h b/src/video_core/shader/shader_uniforms.h index 553d5c579..f10fb11ba 100644 --- a/src/video_core/shader/shader_uniforms.h +++ b/src/video_core/shader/shader_uniforms.h @@ -64,9 +64,10 @@ struct UniformData { alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages alignas(16) Common::Vec4f tev_combiner_buffer_color; alignas(16) Common::Vec4f clip_coef; + alignas(16) Common::Vec3f tex_lod_bias; }; -static_assert(sizeof(UniformData) == 0x4F0, +static_assert(sizeof(UniformData) == 0x500, "The size of the UniformData does not match the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec");