// Copyright 2010 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include "VideoBackends/D3D/Render.h" #include #include #include #include #include #include #include #include #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/MathUtil.h" #include "Core/Core.h" #include "VideoBackends/D3D/BoundingBox.h" #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/DXPipeline.h" #include "VideoBackends/D3D/DXShader.h" #include "VideoBackends/D3D/DXTexture.h" #include "VideoBackends/D3D/SwapChain.h" #include "VideoCommon/BPFunctions.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PostProcessing.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" namespace DX11 { // Nvidia stereo blitting struct defined in "nvstereo.h" from the Nvidia SDK typedef struct _Nv_Stereo_Image_Header { unsigned int dwSignature; unsigned int dwWidth; unsigned int dwHeight; unsigned int dwBPP; unsigned int dwFlags; } NVSTEREOIMAGEHEADER, *LPNVSTEREOIMAGEHEADER; #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale) : ::Renderer(swap_chain ? swap_chain->GetWidth() : 0, swap_chain ? swap_chain->GetHeight() : 0, backbuffer_scale, swap_chain ? swap_chain->GetFormat() : AbstractTextureFormat::Undefined), m_swap_chain(std::move(swap_chain)) { } Renderer::~Renderer() = default; void Renderer::Create3DVisionTexture(int width, int height) { // Create a staging texture for 3D vision with signature information in the last row. // Nvidia 3D Vision supports full SBS, so there is no loss in resolution during this process. NVSTEREOIMAGEHEADER header; header.dwSignature = NVSTEREO_IMAGE_SIGNATURE; header.dwWidth = static_cast(width * 2); header.dwHeight = static_cast(height + 1); header.dwBPP = 32; header.dwFlags = 0; const u32 pitch = static_cast(4 * width * 2); const auto memory = std::make_unique((height + 1) * pitch); u8* image_header_location = &memory[height * pitch]; std::memcpy(image_header_location, &header, sizeof(header)); D3D11_SUBRESOURCE_DATA sys_data; sys_data.SysMemPitch = pitch; sys_data.pSysMem = memory.get(); CD3D11_TEXTURE2D_DESC texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, width * 2, height + 1, 1, 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT, 0, 1, 0, 0); ID3D11Texture2D* texture; HRESULT hr = D3D::device->CreateTexture2D(&texture_desc, &sys_data, &texture); CHECK(SUCCEEDED(hr), "Create 3D Vision Texture"); m_3d_vision_texture = DXTexture::CreateAdopted(texture); m_3d_vision_framebuffer = DXFramebuffer::Create(m_3d_vision_texture.get(), nullptr); } bool Renderer::IsHeadless() const { return !m_swap_chain; } std::unique_ptr Renderer::CreateTexture(const TextureConfig& config) { return DXTexture::Create(config); } std::unique_ptr Renderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& config) { return DXStagingTexture::Create(type, config); } std::unique_ptr Renderer::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) { return DXFramebuffer::Create(static_cast(color_attachment), static_cast(depth_attachment)); } std::unique_ptr Renderer::CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) { DXShader::BinaryData bytecode; if (!DXShader::CompileShader(D3D::feature_level, &bytecode, stage, source, length)) return nullptr; return DXShader::CreateFromBytecode(stage, std::move(bytecode)); } std::unique_ptr Renderer::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length) { return DXShader::CreateFromBytecode(stage, DXShader::CreateByteCode(data, length)); } std::unique_ptr Renderer::CreatePipeline(const AbstractPipelineConfig& config) { return DXPipeline::Create(config); } void Renderer::SetPipeline(const AbstractPipeline* pipeline) { const DXPipeline* dx_pipeline = static_cast(pipeline); if (m_current_pipeline == dx_pipeline) return; if (dx_pipeline) { D3D::stateman->SetRasterizerState(dx_pipeline->GetRasterizerState()); D3D::stateman->SetDepthState(dx_pipeline->GetDepthState()); D3D::stateman->SetBlendState(dx_pipeline->GetBlendState()); D3D::stateman->SetPrimitiveTopology(dx_pipeline->GetPrimitiveTopology()); D3D::stateman->SetInputLayout(dx_pipeline->GetInputLayout()); D3D::stateman->SetVertexShader(dx_pipeline->GetVertexShader()); D3D::stateman->SetGeometryShader(dx_pipeline->GetGeometryShader()); D3D::stateman->SetPixelShader(dx_pipeline->GetPixelShader()); D3D::stateman->SetIntegerRTV(dx_pipeline->UseLogicOp()); } else { // These will be destroyed at pipeline destruction. D3D::stateman->SetInputLayout(nullptr); D3D::stateman->SetVertexShader(nullptr); D3D::stateman->SetGeometryShader(nullptr); D3D::stateman->SetPixelShader(nullptr); } } void Renderer::SetScissorRect(const MathUtil::Rectangle& rc) { // TODO: Move to stateman const CD3D11_RECT rect(rc.left, rc.top, std::max(rc.right, rc.left + 1), std::max(rc.bottom, rc.top + 1)); D3D::context->RSSetScissorRects(1, &rect); } void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, float far_depth) { // TODO: Move to stateman const CD3D11_VIEWPORT vp(x, y, width, height, near_depth, far_depth); D3D::context->RSSetViewports(1, &vp); } void Renderer::Draw(u32 base_vertex, u32 num_vertices) { D3D::stateman->Apply(); D3D::context->Draw(num_vertices, base_vertex); } void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) { D3D::stateman->Apply(); D3D::context->DrawIndexed(num_indices, base_index, base_vertex); } void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y, u32 groups_z) { D3D::stateman->SetComputeShader(static_cast(shader)->GetD3DComputeShader()); D3D::stateman->SyncComputeBindings(); D3D::context->Dispatch(groups_x, groups_y, groups_z); } void Renderer::BindBackbuffer(const ClearColor& clear_color) { CheckForSwapChainChanges(); SetAndClearFramebuffer(m_swap_chain->GetFramebuffer(), clear_color); } void Renderer::PresentBackbuffer() { m_swap_chain->Present(); } void Renderer::OnConfigChanged(u32 bits) { // Quad-buffer changes require swap chain recreation. if (bits & CONFIG_CHANGE_BIT_STEREO_MODE && m_swap_chain) m_swap_chain->SetStereo(SwapChain::WantsStereo()); } void Renderer::CheckForSwapChainChanges() { const bool surface_changed = m_surface_changed.TestAndClear(); const bool surface_resized = m_surface_resized.TestAndClear() || m_swap_chain->CheckForFullscreenChange(); if (!surface_changed && !surface_resized) return; if (surface_changed) { m_swap_chain->ChangeSurface(m_new_surface_handle); m_new_surface_handle = nullptr; } else { m_swap_chain->ResizeSwapChain(); } m_backbuffer_width = m_swap_chain->GetWidth(); m_backbuffer_height = m_swap_chain->GetHeight(); m_3d_vision_framebuffer.reset(); m_3d_vision_texture.reset(); } void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer) { if (m_current_framebuffer == framebuffer) return; // We can't leave the framebuffer bound as a texture and a render target. DXFramebuffer* fb = static_cast(framebuffer); if ((fb->GetColorAttachment() && D3D::stateman->UnsetTexture( static_cast(fb->GetColorAttachment())->GetD3DSRV()) != 0) || (fb->GetDepthAttachment() && D3D::stateman->UnsetTexture( static_cast(fb->GetDepthAttachment())->GetD3DSRV()) != 0)) { D3D::stateman->ApplyTextures(); } D3D::stateman->SetFramebuffer(fb); m_current_framebuffer = fb; } void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) { SetFramebuffer(framebuffer); } void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value, float depth_value) { SetFramebuffer(framebuffer); D3D::stateman->Apply(); if (framebuffer->GetColorFormat() != AbstractTextureFormat::Undefined) { D3D::context->ClearRenderTargetView( static_cast(framebuffer)->GetRTVArray()[0], color_value.data()); } if (framebuffer->GetDepthFormat() != AbstractTextureFormat::Undefined) { D3D::context->ClearDepthStencilView(static_cast(framebuffer)->GetDSV(), D3D11_CLEAR_DEPTH, depth_value, 0); } } void Renderer::SetTexture(u32 index, const AbstractTexture* texture) { D3D::stateman->SetTexture(index, texture ? static_cast(texture)->GetD3DSRV() : nullptr); } void Renderer::SetSamplerState(u32 index, const SamplerState& state) { D3D::stateman->SetSampler(index, m_state_cache.Get(state)); } void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) { D3D::stateman->SetComputeUAV(texture ? static_cast(texture)->GetD3DUAV() : nullptr); } void Renderer::UnbindTexture(const AbstractTexture* texture) { if (D3D::stateman->UnsetTexture(static_cast(texture)->GetD3DSRV()) != 0) D3D::stateman->ApplyTextures(); } u16 Renderer::BBoxRead(int index) { return static_cast(BBox::Get(index)); } void Renderer::BBoxWrite(int index, u16 value) { BBox::Set(index, value); } void Renderer::Flush() { D3D::context->Flush(); } void Renderer::WaitForGPUIdle() { // There is no glFinish() equivalent in D3D. D3D::context->Flush(); } void Renderer::RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) { if (g_ActiveConfig.stereo_mode != StereoMode::Nvidia3DVision) return ::Renderer::RenderXFBToScreen(texture, rc); if (!m_3d_vision_texture) Create3DVisionTexture(m_backbuffer_width, m_backbuffer_height); // Render to staging texture which is double the width of the backbuffer SetAndClearFramebuffer(m_3d_vision_framebuffer.get()); const auto target_rc = GetTargetRectangle(); m_post_processor->BlitFromTexture(target_rc, rc, texture, 0); m_post_processor->BlitFromTexture( MathUtil::Rectangle(target_rc.left + m_backbuffer_width, target_rc.top, target_rc.right + m_backbuffer_width, target_rc.bottom), rc, texture, 1); // Copy the left eye to the backbuffer, if Nvidia 3D Vision is enabled it should // recognize the signature and automatically include the right eye frame. const CD3D11_BOX box(0, 0, 0, m_swap_chain->GetWidth(), m_swap_chain->GetHeight(), 1); D3D::context->CopySubresourceRegion(m_swap_chain->GetTexture()->GetD3DTexture(), 0, 0, 0, 0, m_3d_vision_texture->GetD3DTexture(), 0, &box); // Restore render target to backbuffer SetFramebuffer(m_swap_chain->GetFramebuffer()); } void Renderer::SetFullscreen(bool enable_fullscreen) { if (m_swap_chain) m_swap_chain->SetFullscreen(enable_fullscreen); } bool Renderer::IsFullscreen() const { return m_swap_chain && m_swap_chain->GetFullscreen(); } } // namespace DX11