diff --git a/src/common/vulkan/builders.cpp b/src/common/vulkan/builders.cpp index 608cff256e..3d7baae0e0 100644 --- a/src/common/vulkan/builders.cpp +++ b/src/common/vulkan/builders.cpp @@ -440,6 +440,11 @@ void SamplerBuilder::SetAddressMode(VkSamplerAddressMode u, VkSamplerAddressMode m_ci.addressModeW = w; } +void SamplerBuilder::SetBorderColor(VkBorderColor color) +{ + m_ci.borderColor = color; +} + void SamplerBuilder::SetPointSampler(VkSamplerAddressMode address_mode /* = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER */) { Clear(); diff --git a/src/common/vulkan/builders.h b/src/common/vulkan/builders.h index 25fe634ee3..0a5f7528c8 100644 --- a/src/common/vulkan/builders.h +++ b/src/common/vulkan/builders.h @@ -149,9 +149,10 @@ class SamplerBuilder void SetFilter(VkFilter mag_filter, VkFilter min_filter, VkSamplerMipmapMode mip_filter); void SetAddressMode(VkSamplerAddressMode u, VkSamplerAddressMode v, VkSamplerAddressMode w); + void SetBorderColor(VkBorderColor color); - void SetPointSampler(VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); - void SetLinearSampler(bool mipmaps, VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + void SetPointSampler(VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); + void SetLinearSampler(bool mipmaps, VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); private: VkSamplerCreateInfo m_ci; diff --git a/src/core/gpu_hw_vulkan.cpp b/src/core/gpu_hw_vulkan.cpp index ed345e3237..a1b96d2312 100644 --- a/src/core/gpu_hw_vulkan.cpp +++ b/src/core/gpu_hw_vulkan.cpp @@ -521,7 +521,7 @@ bool GPU_HW_Vulkan::CreateSamplers() VkDevice device = g_vulkan_context->GetDevice(); Vulkan::SamplerBuilder sbuilder; - sbuilder.SetPointSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + sbuilder.SetPointSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); sbuilder.SetAddressMode(VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT); m_point_sampler = sbuilder.Create(device); @@ -529,7 +529,7 @@ bool GPU_HW_Vulkan::CreateSamplers() return false; Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_point_sampler, "Point Sampler"); - sbuilder.SetLinearSampler(false, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + sbuilder.SetLinearSampler(false, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); sbuilder.SetAddressMode(VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT); m_linear_sampler = sbuilder.Create(device); @@ -537,7 +537,7 @@ bool GPU_HW_Vulkan::CreateSamplers() return false; Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_linear_sampler, "Linear Sampler"); - sbuilder.SetLinearSampler(true, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + sbuilder.SetLinearSampler(true, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); m_trilinear_sampler = sbuilder.Create(device); if (m_trilinear_sampler == VK_NULL_HANDLE) return false; diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index ce785dac3b..309f9b99ae 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -655,6 +655,17 @@ bool D3D11HostDisplay::CreateResources() if (FAILED(hr)) return false; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; + sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; + sampler_desc.BorderColor[0] = 0.0f; + sampler_desc.BorderColor[1] = 0.0f; + sampler_desc.BorderColor[2] = 0.0f; + sampler_desc.BorderColor[3] = 1.0f; + hr = m_device->CreateSamplerState(&sampler_desc, m_border_sampler.GetAddressOf()); + if (FAILED(hr)) + return false; + return true; } @@ -665,6 +676,7 @@ void D3D11HostDisplay::DestroyResources() m_post_processing_stages.clear(); m_display_uniform_buffer.Release(); + m_border_sampler.Reset(); m_linear_sampler.Reset(); m_point_sampler.Reset(); m_display_alpha_pixel_shader.Reset(); @@ -1082,7 +1094,7 @@ void D3D11HostDisplay::ApplyPostProcessingChain(ID3D11RenderTargetView* final_ta m_context->VSSetShader(pps.vertex_shader.Get(), nullptr, 0); m_context->PSSetShader(pps.pixel_shader.Get(), nullptr, 0); m_context->PSSetShaderResources(0, 1, texture->GetD3DSRVArray()); - m_context->PSSetSamplers(0, 1, m_point_sampler.GetAddressOf()); + m_context->PSSetSamplers(0, 1, m_border_sampler.GetAddressOf()); const auto map = m_display_uniform_buffer.Map(m_context.Get(), m_display_uniform_buffer.GetSize(), pps.uniforms_size); diff --git a/src/frontend-common/d3d11_host_display.h b/src/frontend-common/d3d11_host_display.h index 350636c8ff..1ce251b480 100644 --- a/src/frontend-common/d3d11_host_display.h +++ b/src/frontend-common/d3d11_host_display.h @@ -132,6 +132,7 @@ class D3D11HostDisplay final : public HostDisplay ComPtr m_display_alpha_pixel_shader; ComPtr m_point_sampler; ComPtr m_linear_sampler; + ComPtr m_border_sampler; D3D11::StreamBuffer m_display_uniform_buffer; ComPtr m_readback_staging_texture; diff --git a/src/frontend-common/d3d12_host_display.cpp b/src/frontend-common/d3d12_host_display.cpp index d1d4935ebd..1aa94773c3 100644 --- a/src/frontend-common/d3d12_host_display.cpp +++ b/src/frontend-common/d3d12_host_display.cpp @@ -565,6 +565,18 @@ bool D3D12HostDisplay::CreateResources() g_d3d12_context->GetDevice()->CreateSampler(&desc, m_linear_sampler.cpu_handle); + if (!g_d3d12_context->GetSamplerHeapManager().Allocate(&m_border_sampler)) + return false; + + desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + desc.BorderColor[0] = 0.0f; + desc.BorderColor[1] = 0.0f; + desc.BorderColor[2] = 0.0f; + desc.BorderColor[3] = 1.0f; + g_d3d12_context->GetDevice()->CreateSampler(&desc, m_border_sampler.cpu_handle); + return true; } @@ -578,6 +590,7 @@ void D3D12HostDisplay::DestroyResources() m_post_processing_root_signature.Reset(); m_readback_staging_texture.Destroy(false); + g_d3d12_context->GetSamplerHeapManager().Free(&m_border_sampler); g_d3d12_context->GetSamplerHeapManager().Free(&m_linear_sampler); g_d3d12_context->GetSamplerHeapManager().Free(&m_point_sampler); m_software_cursor_pipeline.Reset(); @@ -1074,7 +1087,7 @@ void D3D12HostDisplay::ApplyPostProcessingChain(ID3D12GraphicsCommandList* cmdli cmdlist->SetPipelineState(pps.pipeline.Get()); cmdlist->SetGraphicsRootDescriptorTable(1, texture->GetSRVDescriptor()); - cmdlist->SetGraphicsRootDescriptorTable(2, m_point_sampler); + cmdlist->SetGraphicsRootDescriptorTable(2, m_border_sampler); cmdlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); cmdlist->DrawInstanced(3, 1, 0, 0); diff --git a/src/frontend-common/d3d12_host_display.h b/src/frontend-common/d3d12_host_display.h index 4b1f2d13ba..268f13cd41 100644 --- a/src/frontend-common/d3d12_host_display.h +++ b/src/frontend-common/d3d12_host_display.h @@ -124,6 +124,7 @@ class D3D12HostDisplay final : public HostDisplay ComPtr m_software_cursor_pipeline; D3D12::DescriptorHandle m_point_sampler; D3D12::DescriptorHandle m_linear_sampler; + D3D12::DescriptorHandle m_border_sampler; D3D12::Texture m_display_pixels_texture; D3D12::StagingTexture m_readback_staging_texture; diff --git a/src/frontend-common/imgui_impl_vulkan.cpp b/src/frontend-common/imgui_impl_vulkan.cpp index b8391a1e18..d157767e6e 100644 --- a/src/frontend-common/imgui_impl_vulkan.cpp +++ b/src/frontend-common/imgui_impl_vulkan.cpp @@ -457,9 +457,9 @@ static bool ImGui_ImplVulkan_CreateFontSampler(VkDevice device) info.magFilter = VK_FILTER_LINEAR; info.minFilter = VK_FILTER_LINEAR; info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; - info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; - info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; info.minLod = -1000; info.maxLod = 1000; info.maxAnisotropy = 1.0f; diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp index ecb513d09f..9a174a970d 100644 --- a/src/frontend-common/opengl_host_display.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -510,6 +510,20 @@ void main() glGenSamplers(1, &m_display_linear_sampler); glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenSamplers(1, &m_display_border_sampler); + glSamplerParameteri(m_display_border_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(m_display_border_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // If we don't have border clamp.. too bad, just hope for the best. + if (!m_gl_context->IsGLES() || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_NV_texture_border_clamp || + GLAD_GL_EXT_texture_border_clamp || GLAD_GL_OES_texture_border_clamp) + { + static constexpr const float border_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + glSamplerParameteri(m_display_border_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glSamplerParameteri(m_display_border_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(m_display_border_sampler, GL_TEXTURE_BORDER_COLOR, border_color); + } } else { @@ -594,6 +608,11 @@ void OpenGLHostDisplay::DestroyResources() glDeleteVertexArrays(1, &m_display_vao); m_display_vao = 0; } + if (m_display_border_sampler != 0) + { + glDeleteSamplers(1, &m_display_border_sampler); + m_display_border_sampler = 0; + } if (m_display_linear_sampler != 0) { glDeleteSamplers(1, &m_display_linear_sampler); @@ -657,9 +676,9 @@ bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty()) { ApplyPostProcessingChain(texture.GetGLFramebufferID(), left, height - top - draw_height, draw_width, draw_height, - static_cast(m_display_texture), m_display_texture_view_x, - m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, - width, height); + static_cast(m_display_texture), m_display_texture_view_x, + m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height, + width, height); } else { @@ -952,7 +971,7 @@ void OpenGLHostDisplay::ApplyPostProcessingChain(GLuint final_target, s32 final_ pps.program.Bind(); static_cast(texture)->Bind(); - glBindSampler(0, m_display_nearest_sampler); + glBindSampler(0, m_display_border_sampler); const auto map_result = m_post_processing_ubo->Map(m_uniform_buffer_alignment, pps.uniforms_size); m_post_processing_chain.GetShaderStage(i).FillUniformBuffer( diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h index 0f42c513ee..9e693d1566 100644 --- a/src/frontend-common/opengl_host_display.h +++ b/src/frontend-common/opengl_host_display.h @@ -112,6 +112,7 @@ class OpenGLHostDisplay final : public HostDisplay GLuint m_display_vao = 0; GLuint m_display_nearest_sampler = 0; GLuint m_display_linear_sampler = 0; + GLuint m_display_border_sampler = 0; GLuint m_uniform_buffer_alignment = 1; std::unique_ptr m_texture_stream_buffer; diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index d60c6f4dbb..769be0c947 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -507,16 +507,22 @@ void main() vkDestroyShaderModule(device, cursor_fragment_shader, nullptr); Vulkan::SamplerBuilder sbuilder; - sbuilder.SetPointSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + sbuilder.SetPointSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); m_point_sampler = sbuilder.Create(device, true); if (m_point_sampler == VK_NULL_HANDLE) return false; - sbuilder.SetLinearSampler(false, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + sbuilder.SetLinearSampler(false, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); m_linear_sampler = sbuilder.Create(device); if (m_linear_sampler == VK_NULL_HANDLE) return false; + sbuilder.SetPointSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER); + sbuilder.SetBorderColor(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK); + m_border_sampler = sbuilder.Create(device); + if (m_border_sampler == VK_NULL_HANDLE) + return false; + return true; } @@ -536,6 +542,7 @@ void VulkanHostDisplay::DestroyResources() Vulkan::Util::SafeDestroyPipeline(m_cursor_pipeline); Vulkan::Util::SafeDestroyPipelineLayout(m_pipeline_layout); Vulkan::Util::SafeDestroyDescriptorSetLayout(m_descriptor_set_layout); + Vulkan::Util::SafeDestroySampler(m_border_sampler); Vulkan::Util::SafeDestroySampler(m_point_sampler); Vulkan::Util::SafeDestroySampler(m_linear_sampler); } @@ -1149,7 +1156,7 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi } Vulkan::DescriptorSetUpdateBuilder dsupdate; - dsupdate.AddCombinedImageSamplerDescriptorWrite(ds, 1, texture->GetView(), m_point_sampler, texture->GetLayout()); + dsupdate.AddCombinedImageSamplerDescriptorWrite(ds, 1, texture->GetView(), m_border_sampler, texture->GetLayout()); if (use_push_constants) { diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h index ada24886c0..732001319a 100644 --- a/src/frontend-common/vulkan_host_display.h +++ b/src/frontend-common/vulkan_host_display.h @@ -121,6 +121,7 @@ class VulkanHostDisplay final : public HostDisplay VkPipeline m_display_pipeline = VK_NULL_HANDLE; VkSampler m_point_sampler = VK_NULL_HANDLE; VkSampler m_linear_sampler = VK_NULL_HANDLE; + VkSampler m_border_sampler = VK_NULL_HANDLE; VmaAllocation m_readback_staging_allocation = VK_NULL_HANDLE; VkBuffer m_readback_staging_buffer = VK_NULL_HANDLE;