diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp new file mode 100644 index 000000000..679bd7239 --- /dev/null +++ b/src/dxgi/dxgi_presenter.cpp @@ -0,0 +1,317 @@ +#include "dxgi_presenter.h" + +#include "../spirv/spirv_module.h" + +namespace dxvk { + + DxgiPresenter::DxgiPresenter( + const Rc<DxvkDevice>& device, + HWND window, + UINT bufferWidth, + UINT bufferHeight) + : m_device(device) { + + // Create Vulkan surface for the window + HINSTANCE instance = reinterpret_cast<HINSTANCE>( + GetWindowLongPtr(window, GWLP_HINSTANCE)); + + m_surface = m_device->adapter()->createSurface(instance, window); + + // Create swap chain for the surface + DxvkSwapchainProperties swapchainProperties; + swapchainProperties.preferredSurfaceFormat.format = VK_FORMAT_B8G8R8A8_SNORM; + swapchainProperties.preferredSurfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR; + swapchainProperties.preferredBufferSize.width = bufferWidth; + swapchainProperties.preferredBufferSize.height = bufferHeight; + + m_swapchain = m_device->createSwapchain( + m_surface, swapchainProperties); + + // Synchronization semaphores for swap chain operations + m_acquireSync = m_device->createSemaphore(); + m_presentSync = m_device->createSemaphore(); + + // Create context and a command list + m_context = m_device->createContext(); + m_commandList = m_device->createCommandList(); + + // Set up context state. The shader bindings and the + // constant state objects will never be modified. + m_context->bindShader(VK_SHADER_STAGE_VERTEX_BIT, createVertexShader()); + m_context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, createFragmentShader()); + + m_context->setInputAssemblyState( + new DxvkInputAssemblyState( + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + VK_FALSE)); + + m_context->setInputLayout( + new DxvkInputLayout( + 0, nullptr, 0, nullptr)); + + m_context->setRasterizerState( + new DxvkRasterizerState( + VK_FALSE, VK_FALSE, + VK_POLYGON_MODE_FILL, + VK_CULL_MODE_NONE, + VK_FRONT_FACE_COUNTER_CLOCKWISE, + VK_FALSE, 0.0f, 0.0f, 0.0f, 1.0f)); + + m_context->setMultisampleState( + new DxvkMultisampleState( + VK_SAMPLE_COUNT_1_BIT, 0xFFFFFFFF, + VK_FALSE, VK_FALSE, VK_FALSE, 0.0f)); + + VkStencilOpState stencilOp; + stencilOp.failOp = VK_STENCIL_OP_KEEP; + stencilOp.passOp = VK_STENCIL_OP_KEEP; + stencilOp.depthFailOp = VK_STENCIL_OP_KEEP; + stencilOp.compareOp = VK_COMPARE_OP_ALWAYS; + stencilOp.compareMask = 0xFFFFFFFF; + stencilOp.writeMask = 0xFFFFFFFF; + stencilOp.reference = 0; + + m_context->setDepthStencilState( + new DxvkDepthStencilState( + VK_FALSE, VK_FALSE, VK_FALSE, VK_FALSE, + VK_COMPARE_OP_ALWAYS, stencilOp, stencilOp, + 0.0f, 1.0f)); + + VkPipelineColorBlendAttachmentState blendAttachment; + blendAttachment.blendEnable = VK_FALSE; + blendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + blendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + blendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + blendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + blendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + + m_context->setBlendState( + new DxvkBlendState( + VK_FALSE, VK_LOGIC_OP_NO_OP, + 1, &blendAttachment)); + } + + + DxgiPresenter::~DxgiPresenter() { + + } + + + void DxgiPresenter::presentImage(const Rc<DxvkImageView>& view) { + auto framebuffer = m_swapchain->getFramebuffer(m_acquireSync); + auto framebufferSize = framebuffer->size(); + + m_context->beginRecording(m_commandList); + m_context->bindFramebuffer(framebuffer); + + VkViewport viewport; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast<float>(framebufferSize.width); + viewport.height = static_cast<float>(framebufferSize.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = framebufferSize.width; + scissor.extent.height = framebufferSize.height; + + m_context->setViewports(1, &viewport, &scissor); + + // TODO bind back buffer as a shader resource + m_context->draw(4, 1, 0, 0); + m_context->endRecording(); + + m_device->submitCommandList(m_commandList, + m_acquireSync, m_presentSync); + + m_swapchain->present(m_presentSync); + + // FIXME Make sure that the semaphores and the command + // list can be safely used without stalling the device. + m_device->waitForIdle(); + } + + + Rc<DxvkShader> DxgiPresenter::createVertexShader() { + SpirvModule module; + + // Set up basic vertex shader capabilities + module.enableCapability(spv::CapabilityShader); + module.setMemoryModel( + spv::AddressingModelLogical, + spv::MemoryModelGLSL450); + + // ID of the entry point (function) + uint32_t entryPointId = module.allocateId(); + + // Data type definitions + uint32_t typeVoid = module.defVoidType(); + uint32_t typeU32 = module.defIntType(32, 0); + uint32_t typeF32 = module.defFloatType(32); + uint32_t typeVec2 = module.defVectorType(typeF32, 2); + uint32_t typeVec4 = module.defVectorType(typeF32, 4); + uint32_t typeVec4Arr4 = module.defArrayType(typeVec4, module.constu32(4)); + uint32_t typeFn = module.defFunctionType(typeVoid, 0, nullptr); + + // Pointer type definitions + uint32_t ptrInputU32 = module.defPointerType(typeU32, spv::StorageClassInput); + uint32_t ptrOutputVec2 = module.defPointerType(typeVec2, spv::StorageClassOutput); + uint32_t ptrOutputVec4 = module.defPointerType(typeVec4, spv::StorageClassOutput); + uint32_t ptrPrivateVec4 = module.defPointerType(typeVec4, spv::StorageClassPrivate); + uint32_t ptrPrivateArr4 = module.defPointerType(typeVec4Arr4, spv::StorageClassPrivate); + + // Input variable: VertexIndex + uint32_t inVertexId = module.newVar( + ptrInputU32, spv::StorageClassInput); + module.decorateBuiltIn(inVertexId, spv::BuiltInVertexIndex); + + // Output variable: Position + uint32_t outPosition = module.newVar( + ptrOutputVec4, spv::StorageClassOutput); + module.decorateBuiltIn(outPosition, spv::BuiltInPosition); + + // Output variable: Texture coordinates + uint32_t outTexCoord = module.newVar( + ptrOutputVec2, spv::StorageClassOutput); + module.decorateLocation(outTexCoord, 0); + + // Temporary variable: Vertex array + uint32_t varVertexArray = module.newVar( + ptrPrivateArr4, spv::StorageClassPrivate); + + // Scalar constants + uint32_t constF32Zero = module.constf32( 0.0f); + uint32_t constF32Half = module.constf32( 0.5f); + uint32_t constF32Pos1 = module.constf32( 1.0f); + uint32_t constF32Neg1 = module.constf32(-1.0f); + + // Vector constants + uint32_t constVec2HalfIds[2] = { constF32Half, constF32Half }; + uint32_t constVec2Half = module.constComposite(typeVec2, 2, constVec2HalfIds); + + // Construct vertex array + uint32_t vertexData[16] = { + constF32Neg1, constF32Neg1, constF32Zero, constF32Pos1, + constF32Neg1, constF32Pos1, constF32Zero, constF32Pos1, + constF32Pos1, constF32Neg1, constF32Zero, constF32Pos1, + constF32Pos1, constF32Pos1, constF32Zero, constF32Pos1, + }; + + uint32_t vertexConstants[4] = { + module.constComposite(typeVec4, 4, vertexData + 0), + module.constComposite(typeVec4, 4, vertexData + 4), + module.constComposite(typeVec4, 4, vertexData + 8), + module.constComposite(typeVec4, 4, vertexData + 12), + }; + + uint32_t vertexArray = module.constComposite( + typeVec4Arr4, 4, vertexConstants); + + + // Function header + module.functionBegin(typeVoid, entryPointId, typeFn, spv::FunctionControlMaskNone); + module.opLabel(module.allocateId()); + module.opStore(varVertexArray, vertexArray); + + // Load position of the current vertex + uint32_t tmpVertexId = module.opLoad(typeU32, inVertexId); + uint32_t tmpVertexPtr = module.opAccessChain( + ptrPrivateVec4, varVertexArray, 1, &tmpVertexId); + uint32_t tmpVertexPos = module.opLoad(typeVec4, tmpVertexPtr); + module.opStore(outPosition, tmpVertexPos); + + // Compute texture coordinates + uint32_t swizzleIndices[2] = { 0, 1 }; + uint32_t tmpTexCoord = module.opVectorShuffle(typeVec2, + tmpVertexPos, tmpVertexPos, 2, swizzleIndices); + tmpTexCoord = module.opFMul(typeVec2, tmpTexCoord, constVec2Half); + tmpTexCoord = module.opFAdd(typeVec2, tmpTexCoord, constVec2Half); + module.opStore(outTexCoord, tmpTexCoord); + + module.opReturn(); + module.functionEnd(); + + // Register function entry point + std::array<uint32_t, 3> interfaces = { + inVertexId, outPosition, outTexCoord, + }; + + module.addEntryPoint(entryPointId, spv::ExecutionModelVertex, + "main", interfaces.size(), interfaces.data()); + + // Create the actual shader module + return m_device->createShader( + VK_SHADER_STAGE_VERTEX_BIT, module.compile()); + } + + + Rc<DxvkShader> DxgiPresenter::createFragmentShader() { + SpirvModule module; + + module.enableCapability(spv::CapabilityShader); + module.setMemoryModel( + spv::AddressingModelLogical, + spv::MemoryModelGLSL450); + + uint32_t entryPointId = module.allocateId(); + + // Data type definitions + uint32_t typeVoid = module.defVoidType(); + uint32_t typeF32 = module.defFloatType(32); + uint32_t typeVec2 = module.defVectorType(typeF32, 2); + uint32_t typeVec4 = module.defVectorType(typeF32, 4); + uint32_t typeFn = module.defFunctionType(typeVoid, 0, nullptr); + + // Pointer type definitions + uint32_t ptrInputVec2 = module.defPointerType(typeVec2, spv::StorageClassInput); + uint32_t ptrOutputVec4 = module.defPointerType(typeVec4, spv::StorageClassOutput); + + // Input variable: Texture coordinates + uint32_t inTexCoord = module.newVar( + ptrInputVec2, spv::StorageClassInput); + module.decorateLocation(inTexCoord, 0); + + // Output variable: Final color + uint32_t outColor = module.newVar( + ptrOutputVec4, spv::StorageClassOutput); + module.decorateLocation(outColor, 0); + + // Function header + module.functionBegin(typeVoid, entryPointId, typeFn, spv::FunctionControlMaskNone); + module.opLabel(module.allocateId()); + + // Load texture coordinates + uint32_t tmpTexCoord = module.opLoad(typeVec2, inTexCoord); + + // Compute final color + uint32_t swizzleIndices[4] = { 0, 1, 2, 3 }; + uint32_t tmpColor = module.opVectorShuffle( + typeVec4, tmpTexCoord, tmpTexCoord, 4, swizzleIndices); + module.opStore(outColor, tmpColor); + + module.opReturn(); + module.functionEnd(); + + + // Register function entry point + std::array<uint32_t, 2> interfaces = { inTexCoord, outColor }; + + module.addEntryPoint(entryPointId, spv::ExecutionModelFragment, + "main", interfaces.size(), interfaces.data()); + + + // Create the actual shader module + return m_device->createShader( + VK_SHADER_STAGE_FRAGMENT_BIT, module.compile()); + } + +} diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h new file mode 100644 index 000000000..718fad349 --- /dev/null +++ b/src/dxgi/dxgi_presenter.h @@ -0,0 +1,55 @@ +#pragma once + +#include <dxvk_device.h> +#include <dxvk_surface.h> +#include <dxvk_swapchain.h> + +#include "../spirv/spirv_module.h" + +namespace dxvk { + + /** + * \brief DXGI presenter + * + * Renders the back buffer from the + * \ref DxgiSwapChain to the Vulkan + * swap chain. + */ + class DxgiPresenter : public RcObject { + + public: + + DxgiPresenter( + const Rc<DxvkDevice>& device, + HWND window, + UINT bufferWidth, + UINT bufferHeight); + + ~DxgiPresenter(); + + /** + * \brief Renders image to the screen + * \param [in] view Source image view + */ + void presentImage( + const Rc<DxvkImageView>& view); + + private: + + Rc<DxvkDevice> m_device; + + Rc<DxvkSurface> m_surface; + Rc<DxvkSwapchain> m_swapchain; + + Rc<DxvkSemaphore> m_acquireSync; + Rc<DxvkSemaphore> m_presentSync; + + Rc<DxvkContext> m_context; + Rc<DxvkCommandList> m_commandList; + + Rc<DxvkShader> createVertexShader(); + Rc<DxvkShader> createFragmentShader(); + + }; + +} diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index daf83cf47..bc8d6a227 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -51,36 +51,7 @@ namespace dxvk { if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr))) throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial fullscreen state"); - // TODO clean up here - Com<IDXGIDevicePrivate> dxgiDevice; - m_device->GetDevice(__uuidof(IDXGIDevicePrivate), - reinterpret_cast<void**>(&dxgiDevice)); - - Rc<DxvkDevice> dxvkDevice = dxgiDevice->GetDXVKDevice(); - Rc<DxvkAdapter> dxvkAdapter = dxvkDevice->adapter(); - - m_context = dxvkDevice->createContext(); - m_commandList = dxvkDevice->createCommandList(); - - m_acquireSync = dxvkDevice->createSemaphore(); - m_presentSync = dxvkDevice->createSemaphore(); - - HINSTANCE instance = reinterpret_cast<HINSTANCE>( - GetWindowLongPtr(m_desc.OutputWindow, GWLP_HINSTANCE)); - - m_surface = dxvkAdapter->createSurface( - instance, m_desc.OutputWindow); - - DxvkSwapchainProperties swapchainProperties; - swapchainProperties.preferredSurfaceFormat.format = VK_FORMAT_B8G8R8A8_SNORM; - swapchainProperties.preferredSurfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; - swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR; - swapchainProperties.preferredBufferSize.width = m_desc.BufferDesc.Width; - swapchainProperties.preferredBufferSize.height = m_desc.BufferDesc.Height; - - m_swapchain = dxvkDevice->createSwapchain( - m_surface, swapchainProperties); - + this->createPresenter(); this->createBackBuffer(); } @@ -206,43 +177,7 @@ namespace dxvk { // TODO implement sync interval // TODO implement flags - auto dxvkDevice = dxgiDevice->GetDXVKDevice(); - - auto framebuffer = m_swapchain->getFramebuffer(m_acquireSync); - auto framebufferSize = framebuffer->size(); - - m_context->beginRecording(m_commandList); - m_context->bindFramebuffer(framebuffer); - - // TODO render back buffer into the swap image, - // the clear operation is only a placeholder. - VkClearAttachment clearAttachment; - clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - clearAttachment.colorAttachment = 0; - clearAttachment.clearValue.color.float32[0] = 1.0f; - clearAttachment.clearValue.color.float32[1] = 1.0f; - clearAttachment.clearValue.color.float32[2] = 1.0f; - clearAttachment.clearValue.color.float32[3] = 1.0f; - - VkClearRect clearArea; - clearArea.rect = VkRect2D { { 0, 0 }, framebufferSize.width, framebufferSize.height }; - clearArea.baseArrayLayer = 0; - clearArea.layerCount = framebufferSize.layers; - - m_context->clearRenderTarget( - clearAttachment, - clearArea); - - m_context->endRecording(); - - dxvkDevice->submitCommandList(m_commandList, - m_acquireSync, m_presentSync); - - m_swapchain->present(m_presentSync); - - // FIXME Make sure that the semaphores and the command - // list can be safely used without stalling the device. - dxvkDevice->waitForIdle(); + m_presenter->presentImage(m_backBufferView); return S_OK; } catch (const DxvkError& err) { Logger::err(err.message()); @@ -356,6 +291,19 @@ namespace dxvk { } + void DxgiSwapChain::createPresenter() { + Com<IDXGIDevicePrivate> dxgiDevice; + m_device->GetDevice(__uuidof(IDXGIDevicePrivate), + reinterpret_cast<void**>(&dxgiDevice)); + + m_presenter = new DxgiPresenter( + dxgiDevice->GetDXVKDevice(), + m_desc.OutputWindow, + m_desc.BufferDesc.Width, + m_desc.BufferDesc.Height); + } + + void DxgiSwapChain::createBackBuffer() { // TODO select format based on DXGI format // TODO support proper multi-sampling diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index c92298dd4..3b7e3bf1d 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -8,9 +8,12 @@ #include "dxgi_interfaces.h" #include "dxgi_object.h" +#include "dxgi_presenter.h" #include "../d3d11/d3d11_interfaces.h" +#include "../spirv/spirv_module.h" + namespace dxvk { class DxgiFactory; @@ -87,7 +90,8 @@ namespace dxvk { DXGI_SWAP_CHAIN_DESC m_desc; DXGI_FRAME_STATISTICS m_stats; - SDL_Window* m_window = nullptr; + SDL_Window* m_window = nullptr; + Rc<DxvkContext> m_context; Rc<DxvkCommandList> m_commandList; @@ -97,12 +101,17 @@ namespace dxvk { Rc<DxvkSemaphore> m_acquireSync; Rc<DxvkSemaphore> m_presentSync; + Rc<DxgiPresenter> m_presenter; + Rc<DxvkImage> m_backBuffer; Rc<DxvkImageView> m_backBufferView; Com<IUnknown> m_backBufferIface; + void createPresenter(); void createBackBuffer(); + void createContext(); + }; } diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build index 2049667b6..bc8883bb6 100644 --- a/src/dxgi/meson.build +++ b/src/dxgi/meson.build @@ -4,6 +4,7 @@ dxgi_src = [ 'dxgi_factory.cpp', 'dxgi_main.cpp', 'dxgi_output.cpp', + 'dxgi_presenter.cpp', 'dxgi_resource.cpp', 'dxgi_swapchain.cpp', ] diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index b489dac32..2feac72fb 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -595,6 +595,21 @@ namespace dxvk { } + uint32_t SpirvModule::opFMul( + uint32_t resultType, + uint32_t a, + uint32_t b) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpFMul, 5); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(a); + m_code.putWord(b); + return resultId; + } + + uint32_t SpirvModule::opFClamp( uint32_t resultType, uint32_t x, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 97d7f395e..a8c8dcf22 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -211,6 +211,11 @@ namespace dxvk { uint32_t a, uint32_t b); + uint32_t opFMul( + uint32_t resultType, + uint32_t a, + uint32_t b); + uint32_t opFClamp( uint32_t resultType, uint32_t x,