diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index f6b83ddf..ad8c0ea0 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -8,8 +8,8 @@ namespace dxvk { DxgiPresenter::DxgiPresenter( - const Rc& device, - HWND window) + const Rc& device, + HWND window) : m_device (device), m_context (device->createContext()) { @@ -25,6 +25,18 @@ namespace dxvk { m_options.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR; m_options.preferredBufferSize = { 0u, 0u }; + // Uniform buffer that stores the gamma ramp + DxvkBufferCreateInfo gammaBufferInfo; + gammaBufferInfo.size = sizeof(DxgiPresenterGammaRamp); + gammaBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + gammaBufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT + | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + gammaBufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT + | VK_ACCESS_SHADER_READ_BIT; + m_gammaBuffer = m_device->createBuffer( + gammaBufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + // Sampler for presentation DxvkSamplerCreateInfo samplerInfo; samplerInfo.magFilter = VK_FILTER_NEAREST; @@ -133,6 +145,9 @@ namespace dxvk { void DxgiPresenter::initBackBuffer(const Rc& image) { + m_context->beginRecording( + m_device->createCommandList()); + VkImageSubresourceRange sr; sr.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; sr.baseMipLevel = 0; @@ -140,9 +155,8 @@ namespace dxvk { sr.baseArrayLayer = 0; sr.layerCount = image->info().numLayers; - m_context->beginRecording( - m_device->createCommandList()); m_context->initImage(image, sr); + m_device->submitCommandList( m_context->endRecording(), nullptr, nullptr); @@ -209,6 +223,8 @@ namespace dxvk { m_context->bindResourceView(BindingIds::Texture, m_backBufferView, nullptr); m_context->draw(4, 1, 0, 0); + m_context->bindResourceBuffer(BindingIds::GammaUbo, DxvkBufferSlice(m_gammaBuffer)); + if (m_hud != nullptr) { m_blendMode.enableBlending = VK_TRUE; m_context->setBlendMode(0, m_blendMode); @@ -273,7 +289,6 @@ namespace dxvk { : m_backBuffer, viewInfo); - // TODO move this elsewhere this->initBackBuffer(m_backBuffer); } @@ -344,6 +359,20 @@ namespace dxvk { } + void DxgiPresenter::setGammaRamp(const DxgiPresenterGammaRamp& data) { + m_context->beginRecording( + m_device->createCommandList()); + + m_context->updateBuffer(m_gammaBuffer, + 0, sizeof(DxgiPresenterGammaRamp), + &data); + + m_device->submitCommandList( + m_context->endRecording(), + nullptr, nullptr); + } + + Rc DxgiPresenter::createVertexShader() { const SpirvCodeBuffer codeBuffer(dxgi_presenter_vert); @@ -358,9 +387,10 @@ namespace dxvk { const SpirvCodeBuffer codeBuffer(dxgi_presenter_frag); // Shader resource slots - std::array resourceSlots = {{ - { BindingIds::Sampler, VK_DESCRIPTOR_TYPE_SAMPLER, VK_IMAGE_VIEW_TYPE_MAX_ENUM }, - { BindingIds::Texture, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_VIEW_TYPE_2D }, + const std::array resourceSlots = {{ + { BindingIds::Sampler, VK_DESCRIPTOR_TYPE_SAMPLER, VK_IMAGE_VIEW_TYPE_MAX_ENUM }, + { BindingIds::Texture, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_VIEW_TYPE_2D }, + { BindingIds::GammaUbo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM }, }}; // Create the actual shader module diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h index 348b89ed..76d75d02 100644 --- a/src/dxgi/dxgi_presenter.h +++ b/src/dxgi/dxgi_presenter.h @@ -12,6 +12,25 @@ namespace dxvk { + /** + * \brief Gamma ramp + * + * Structure that can be used to set the gamma + * ramp of a swap chain. This is the same data + * structure that is used by the fragment shader. + */ + struct DxgiPresenterGammaRamp { + constexpr static uint32_t CpCount = 1025; + + float in_factor[4]; + float in_offset[4]; + float cp_values[4 * CpCount]; + + static float cpLocation(uint32_t cp) { + return float(cp) / float(CpCount - 1); + } + }; + /** * \brief DXGI presenter * @@ -24,8 +43,8 @@ namespace dxvk { public: DxgiPresenter( - const Rc& device, - HWND window); + const Rc& device, + HWND window); ~DxgiPresenter(); @@ -80,11 +99,18 @@ namespace dxvk { */ VkPresentModeKHR pickPresentMode(VkPresentModeKHR preferred) const; + /** + * \brief Sets gamma ramp + * \param [in] data Gamma data + */ + void setGammaRamp(const DxgiPresenterGammaRamp& data); + private: enum BindingIds : uint32_t { - Sampler = 0, - Texture = 1, + Sampler = 0, + Texture = 1, + GammaUbo = 2, }; Rc m_device; @@ -93,6 +119,8 @@ namespace dxvk { Rc m_surface; Rc m_swapchain; + Rc m_gammaBuffer; + Rc m_samplerFitting; Rc m_samplerScaling; diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index aec79519..5cb1a563 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -51,6 +51,9 @@ namespace dxvk { if (FAILED(CreatePresenter()) || FAILED(CreateBackBuffer())) throw DxvkError("DxgiSwapChain: Failed to create presenter or back buffer"); + + if (FAILED(SetDefaultGammaRamp())) + throw DxvkError("DxgiSwapChain: Failed to set up gamma ramp"); } @@ -265,6 +268,28 @@ namespace dxvk { } + HRESULT DxgiSwapChain::SetDefaultGammaRamp() { + std::lock_guard lock(m_mutex); + + for (uint32_t i = 0; i < 4; i++) { + m_gammaControl.in_factor[i] = 1.0f; + m_gammaControl.in_offset[i] = 0.0f; + } + + for (uint32_t i = 0; i < DxgiPresenterGammaRamp::CpCount; i++) { + const float value = DxgiPresenterGammaRamp::cpLocation(i); + + m_gammaControl.cp_values[4 * i + 0] = value; + m_gammaControl.cp_values[4 * i + 1] = value; + m_gammaControl.cp_values[4 * i + 2] = value; + m_gammaControl.cp_values[4 * i + 3] = value; + } + + m_presenter->setGammaRamp(m_gammaControl); + return S_OK; + } + + HRESULT DxgiSwapChain::CreatePresenter() { try { m_presenter = new DxgiPresenter( diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index 1ec2cc20..e2804ccd 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -79,7 +79,9 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE SetFullscreenState( BOOL Fullscreen, IDXGIOutput *pTarget) final; - + + HRESULT SetDefaultGammaRamp(); + private: struct WindowState { @@ -93,16 +95,18 @@ namespace dxvk { Com m_factory; Com m_adapter; Com m_device; - Com m_presentDevice; + Com m_presentDevice; DXGI_SWAP_CHAIN_DESC m_desc; DXGI_FRAME_STATISTICS m_stats; Rc m_presenter; - Com m_backBuffer; + Com m_backBuffer; WindowState m_windowState; + DxgiPresenterGammaRamp m_gammaControl; + HRESULT CreatePresenter(); HRESULT CreateBackBuffer(); diff --git a/src/dxgi/shaders/dxgi_presenter_frag.frag b/src/dxgi/shaders/dxgi_presenter_frag.frag index 831862aa..49859467 100644 --- a/src/dxgi/shaders/dxgi_presenter_frag.frag +++ b/src/dxgi/shaders/dxgi_presenter_frag.frag @@ -1,11 +1,43 @@ #version 450 +#define CP_COUNT 1025 + layout(binding = 0) uniform sampler s_sampler; layout(binding = 1) uniform texture2D t_texture; +layout(binding = 2) +uniform u_gamma_ramp_t { + layout(offset = 0) vec4 in_factor; + layout(offset = 16) vec4 in_offset; + layout(offset = 32) vec4 cp_values[CP_COUNT + 1]; +} u_gamma_ramp; + layout(location = 0) in vec2 i_texcoord; layout(location = 0) out vec4 o_color; void main() { o_color = texture(sampler2D(t_texture, s_sampler), i_texcoord); + + vec3 cp_lookup = o_color.rgb; + cp_lookup *= u_gamma_ramp.in_factor.rgb; + cp_lookup += u_gamma_ramp.in_offset.rgb; + + cp_lookup = clamp( + cp_lookup * float(CP_COUNT - 1), + 0.0f, float(CP_COUNT - 1)); + + vec3 cp_fpart = fract(cp_lookup); + ivec3 cp_index = ivec3(cp_lookup); + + for (int i = 0; i < 3; i++) { + int cp_entry = cp_index[i]; + + float lo = u_gamma_ramp.cp_values[cp_entry + 0][i]; + float hi = u_gamma_ramp.cp_values[cp_entry + 1][i]; + + if (cp_entry == CP_COUNT - 1) + hi = lo; + + o_color[i] = mix(lo, hi, cp_fpart[i]); + } } \ No newline at end of file