diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index 44d3edc..52e20da 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -44,9 +44,20 @@ if(UNIX) endif() else() list(APPEND graphics_LIB_INCLUDES - directx/DirectXPainter.cpp) + directx/DirectXPainter.cpp + directx/DirectXTextPainter.cpp + directx/DirectXMeshPainter.cpp + directx/DirectXShaderProgram.cpp + ) list(APPEND graphics_HEADERS - directx/DirectXPainter.h) + directx/DirectXPainter.h + directx/DirectXTextPainter.h + directx/DirectXMeshPainter.h + directx/DirectXShaderProgram.h + ) + + find_package(DirectX-Headers REQUIRED) + list(APPEND platform_LIBS D3D12.lib D3DCompiler.lib Dwrite.lib D2d1.lib Microsoft::DirectX-Headers) endif() add_library(${MODULE_NAME} SHARED @@ -57,6 +68,7 @@ target_include_directories(${MODULE_NAME} PUBLIC ${platform_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/opengl + ${CMAKE_CURRENT_SOURCE_DIR}/directx ) target_compile_definitions(${MODULE_NAME} PRIVATE ${DEFINES}) diff --git a/src/graphics/DrawingContext.cpp b/src/graphics/DrawingContext.cpp index cdf44fb..3e7285b 100644 --- a/src/graphics/DrawingContext.cpp +++ b/src/graphics/DrawingContext.cpp @@ -41,3 +41,8 @@ void DrawingContext::paint() mPainter->paint(this); } + +AbstractPainter* DrawingContext::getPainter() const +{ + return mPainter.get(); +} diff --git a/src/graphics/DrawingContext.h b/src/graphics/DrawingContext.h index ab02755..7034cee 100644 --- a/src/graphics/DrawingContext.h +++ b/src/graphics/DrawingContext.h @@ -26,6 +26,8 @@ public: void paint(); + AbstractPainter* getPainter() const; + private: DrawingMode mDrawingMode; FontsManager* mFontsManager{nullptr}; diff --git a/src/graphics/PainterFactory.cpp b/src/graphics/PainterFactory.cpp index 32bc245..76cc16c 100644 --- a/src/graphics/PainterFactory.cpp +++ b/src/graphics/PainterFactory.cpp @@ -8,6 +8,13 @@ #include "OpenGlFontTexture.h" #endif +#ifdef _WIN32 +#include "DirectXPainter.h" +//#include "DirectXMeshPainter.h" +//#include "DirectXTextPainter.h" +//#include "DirectXShaderProgram.h" +#endif + #include "Grid.h" #include "RasterPainter.h" @@ -16,17 +23,28 @@ std::unique_ptr PainterFactory::Create(DrawingMode drawMode) { -#ifdef HAS_OPENGL +#ifdef _WIN32 if (drawMode == DrawingMode::GRAPH) { - return std::make_unique(); + return std::make_unique(); } else { return std::make_unique(); } #else - return std::make_unique(); + #ifdef HAS_OPENGL + if (drawMode == DrawingMode::GRAPH) + { + return std::make_unique(); + } + else + { + return std::make_unique(); + } + #else + return std::make_unique(); + #endif #endif } diff --git a/src/graphics/directx/DirectXMeshPainter.cpp b/src/graphics/directx/DirectXMeshPainter.cpp new file mode 100644 index 0000000..4d1dd9b --- /dev/null +++ b/src/graphics/directx/DirectXMeshPainter.cpp @@ -0,0 +1,242 @@ +#include "DirectXMeshPainter.h" + +#include "DrawingContext.h" +#include "DrawingSurface.h" +#include "SceneModel.h" + +#include "TriMesh.h" +#include "LineMesh.h" + +#include "DirectXShaderProgram.h" + +#include +#include +#include + +#include + +DirectXMeshPainter::DirectXMeshPainter() +{ + const float aspectRatio = 1.0; + mVertexBuffer = + { + { { 0.0f, 0.25f * aspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, + { { 0.25f, -0.25f * aspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, + { { -0.25f, -0.25f * aspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } + }; + + auto shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders.hlsl"; + mShaderProgram = std::make_unique(shader_path, shader_path); +} + +void DirectXMeshPainter::initializeShader() +{ + +} + +DirectXShaderProgram* DirectXMeshPainter::getShaderProgram() const +{ + return mShaderProgram.get(); +} + +unsigned int DirectXMeshPainter::getVertexBufferSize() const +{ + return mVertexBuffer.size()*sizeof(Vertex); +} + +unsigned int DirectXMeshPainter::getVertexSize() const +{ + return sizeof(Vertex); +} + +ID3D12PipelineState* DirectXMeshPainter::getPipelineState() const +{ + return mPipelineState.Get(); +} + +ID3D12RootSignature* DirectXMeshPainter::getRootSignature() const +{ + return mRootSignature.Get(); +} + +void DirectXMeshPainter::updateVertexBuffer(unsigned char* pBuffer) const +{ + memcpy(pBuffer, mVertexBuffer.data(), getVertexBufferSize()); +} + +void DirectXMeshPainter::updateCommandList(const CD3DX12_CPU_DESCRIPTOR_HANDLE& rtvHandle, ID3D12GraphicsCommandList* commandList) +{ + const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; + commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); + commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + commandList->IASetVertexBuffers(0, 1, &mVertexBufferView); + commandList->DrawInstanced(3, 1, 0, 0); +} + +void DirectXMeshPainter::initializeBuffers() +{ + /* + glGenBuffers(1, &mVertexBuffer); + + glGenBuffers(1, &mElementBuffer); + + glGenVertexArrays(1, &mVertexArray); + */ +} + +void DirectXMeshPainter::paint(const std::vector& verts, const std::vector& elements, const std::vector& color, bool lines) +{ + /* + glBindVertexArray(mVertexArray); + + glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(float), verts.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(unsigned), elements.data(), GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + //glm::mat4 projection = glm::ortho(0.0f, width, 0.0f, height); + //glUniformMatrix4fv(glGetUniformLocation(mShaderProgram->getHandle(), "projection"), 1, GL_FALSE, glm::value_ptr(projection)); + + glUseProgram(mShaderProgram->getHandle()); + glBindVertexArray(mVertexArray); + + int vertexColorLocation = glGetUniformLocation(mShaderProgram->getHandle(), "ourColor"); + glUniform4f(vertexColorLocation, float(color[0]), float(color[1]), float(color[2]), float(color[3])); + + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + if (lines) + { + glDrawElements(GL_LINES, elements.size(), GL_UNSIGNED_INT, 0); + } + else + { + glDrawElements(GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, 0); + } + glBindVertexArray(0); + */ +} + +void DirectXMeshPainter::paint(SceneModel* model, DrawingContext* context) +{ + return; + /* + if (!mShaderProgram) + { + initializeShader(); + } + + if (mVertexArray == 0) + { + initializeBuffers(); + } + + auto surface = context->getSurface(); + const auto width = float(surface->getWidth()); + const auto height = float(surface->getHeight()); + + auto transform = model->getTransform(); + auto vertices = model->getMesh()->getVerticesFlat(); + for (std::size_t idx = 0; idx < vertices.size(); idx++) + { + if (idx % 3 == 0) + { + auto x = vertices[idx] * transform.getScaleX() + transform.getLocation().getX(); + vertices[idx] = 2 * x / width - 1.0; + } + else if (idx % 3 == 1) + { + auto y = vertices[idx] * transform.getScaleY() + transform.getLocation().getY(); + vertices[idx] = 1.0 - 2 * y / height; + } + } + + std::vector indices; + const bool line_mesh = model->getMesh()->getType() == AbstractMesh::MeshType::LINE; + if (line_mesh) + { + indices = dynamic_cast(model->getMesh())->getEdgeNodeIds(); + } + else + { + indices = dynamic_cast(model->getMesh())->getFaceNodeIds(); + } + + auto model_color = model->getColor().getAsVectorDouble(); + std::vector color = { float(model_color[0]), float(model_color[1]), float(model_color[2]), float(model_color[3]) }; + + paint(vertices, indices, color, line_mesh); + + if (model->getShowOutline()) + { + auto edge_indices = dynamic_cast(model->getMesh())->getEdgeNodeIds(); + paint(vertices, edge_indices, { 0, 0, 0, 1 }, true); + } + */ +} + +void DirectXMeshPainter::createVertexBuffer(ID3D12Device* device) +{ + // Note: using upload heaps to transfer static data like vert buffers is not + // recommended. Every time the GPU needs it, the upload heap will be marshalled + // over. Please read up on Default Heap usage. An upload heap is used here for + // code simplicity and because there are very few verts to actually transfer. + CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD); + auto desc = CD3DX12_RESOURCE_DESC::Buffer(getVertexBufferSize()); + device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&mD3dVertexBuffer)); + + // Copy the mesh data to the vertex buffer. + UINT8* pVertexDataBegin; + CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. + mD3dVertexBuffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin)); + updateVertexBuffer(pVertexDataBegin); + mD3dVertexBuffer->Unmap(0, nullptr); + + // Initialize the vertex buffer view. + mVertexBufferView.BufferLocation = mD3dVertexBuffer->GetGPUVirtualAddress(); + mVertexBufferView.StrideInBytes = getVertexSize(); + mVertexBufferView.SizeInBytes = getVertexBufferSize(); +} + +void DirectXMeshPainter::createRootSignature(ID3D12Device* device) +{ + CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + Microsoft::WRL::ComPtr signature; + Microsoft::WRL::ComPtr error; + D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error); + device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)); +} + +void DirectXMeshPainter::createPipelineStateObject(ID3D12Device* device) +{ + auto vert_shader = mShaderProgram->getVertexShader(); + auto pixel_shader = mShaderProgram->getPixelShader(); + + D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } + }; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; + psoDesc.pRootSignature = mRootSignature.Get(); + psoDesc.VS = CD3DX12_SHADER_BYTECODE(vert_shader); + psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixel_shader); + psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); + psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); + psoDesc.DepthStencilState.DepthEnable = FALSE; + psoDesc.DepthStencilState.StencilEnable = FALSE; + psoDesc.SampleMask = UINT_MAX; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.NumRenderTargets = 1; + psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; + psoDesc.SampleDesc.Count = 1; + device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPipelineState)); +} diff --git a/src/graphics/directx/DirectXMeshPainter.h b/src/graphics/directx/DirectXMeshPainter.h new file mode 100644 index 0000000..1297f69 --- /dev/null +++ b/src/graphics/directx/DirectXMeshPainter.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include +#include + +class DrawingContext; +class DirectXShaderProgram; +class SceneModel; + +struct CD3DX12_CPU_DESCRIPTOR_HANDLE; + +class DirectXMeshPainter +{ +public: + DirectXMeshPainter(); + + void paint(SceneModel* model, DrawingContext* context); + + DirectXShaderProgram* getShaderProgram() const; + + void createVertexBuffer(ID3D12Device* device); + void createRootSignature(ID3D12Device* device); + void createPipelineStateObject(ID3D12Device* device); + + ID3D12PipelineState* getPipelineState() const; + ID3D12RootSignature* getRootSignature() const; + + void updateCommandList(const CD3DX12_CPU_DESCRIPTOR_HANDLE& rtvHandle, ID3D12GraphicsCommandList* commandList); + +private: + struct Vertex + { + DirectX::XMFLOAT3 position; + DirectX::XMFLOAT4 color; + }; + + void initializeShader(); + void initializeBuffers(); + + unsigned int getVertexBufferSize() const; + unsigned int getVertexSize() const; + void updateVertexBuffer(unsigned char* pBuffer) const; + + void paint(const std::vector& verts, const std::vector& elements, const std::vector& color, bool lines = false); + + unsigned int mVertexBufferSize{ 0 }; + std::vector mVertexBuffer; + + Microsoft::WRL::ComPtr mD3dVertexBuffer; + D3D12_VERTEX_BUFFER_VIEW mVertexBufferView{}; + + Microsoft::WRL::ComPtr mRootSignature; + Microsoft::WRL::ComPtr mPipelineState; + + std::unique_ptr mShaderProgram; +}; \ No newline at end of file diff --git a/src/graphics/directx/DirectXPainter.cpp b/src/graphics/directx/DirectXPainter.cpp index e69de29..12d1519 100644 --- a/src/graphics/directx/DirectXPainter.cpp +++ b/src/graphics/directx/DirectXPainter.cpp @@ -0,0 +1,73 @@ +#include "DirectXPainter.h" + +#include "DrawingContext.h" +#include "DrawingSurface.h" +#include "Scene.h" +#include "SceneItem.h" +#include "SceneModel.h" +#include "SceneText.h" + +#include "TriMesh.h" + +#include "FontsManager.h" +#include "FontGlyph.h" +#include "TextData.h" + +#include "DirectXShaderProgram.h" +#include "DirectXMeshPainter.h" +#include "DirectXTextPainter.h" + +#include "File.h" + +#include + +#include + +DirectXPainter::DirectXPainter() + : mMeshPainter(std::make_unique()), + mTextPainter(std::make_unique()) +{ + +} + +DirectXMeshPainter* DirectXPainter::getMeshPainter() const +{ + return mMeshPainter.get(); +} + +DirectXTextPainter* DirectXPainter::getTextPainter() const +{ + return mTextPainter.get(); +} + +void DirectXPainter::paint(DrawingContext* context) +{ + auto surface = context->getSurface(); + const auto width = double(surface->getWidth()); + const auto height = double(surface->getHeight()); + + /* + glViewport(0, 0, width, height); + glOrtho(0, width, 0, height, -1.0, 1.0); + + glClearColor(0.5, 0.5, 1.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + */ + + auto scene = context->getSurface()->getScene(); + for (const auto item : scene->getItems()) + { + if (item->getType() == SceneItem::Type::MODEL) + { + mMeshPainter->paint(dynamic_cast(item), context); + } + else if (item->getType() == SceneItem::Type::TEXT) + { + mTextPainter->paint(dynamic_cast(item), context); + } + } + + /* + glFlush(); + */ +} diff --git a/src/graphics/directx/DirectXPainter.h b/src/graphics/directx/DirectXPainter.h index e69de29..9027e3d 100644 --- a/src/graphics/directx/DirectXPainter.h +++ b/src/graphics/directx/DirectXPainter.h @@ -0,0 +1,22 @@ +#pragma once + +#include "AbstractPainter.h" +#include + +class DrawingContext; +class DirectXMeshPainter; +class DirectXTextPainter; + +class DirectXPainter : public AbstractPainter +{ +public: + DirectXPainter(); + void paint(DrawingContext* context) override; + + DirectXMeshPainter* getMeshPainter() const; + DirectXTextPainter* getTextPainter() const; + +private: + std::unique_ptr mMeshPainter; + std::unique_ptr mTextPainter; +}; \ No newline at end of file diff --git a/src/graphics/directx/DirectXShaderProgram.cpp b/src/graphics/directx/DirectXShaderProgram.cpp new file mode 100644 index 0000000..1f5bcbe --- /dev/null +++ b/src/graphics/directx/DirectXShaderProgram.cpp @@ -0,0 +1,31 @@ +#include "DirectXShaderProgram.h" + +#include "File.h" +#include "FileLogger.h" + +#include +#include + +DirectXShaderProgram::DirectXShaderProgram(const Path& vertShaderPath, const Path& fragShaderPath) +{ +#if defined(_DEBUG) + // Enable better shader debugging with the graphics debugging tools. + // UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; + UINT compileFlags = 0; +#else + UINT compileFlags = 0; +#endif + + D3DCompileFromFile(vertShaderPath.c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &mVertexShader, nullptr); + D3DCompileFromFile(fragShaderPath.c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &mPixelShader, nullptr); +} + +ID3DBlob* DirectXShaderProgram::getVertexShader() const +{ + return mVertexShader.Get(); +} + +ID3DBlob* DirectXShaderProgram::getPixelShader() const +{ + return mPixelShader.Get(); +} \ No newline at end of file diff --git a/src/graphics/directx/DirectXShaderProgram.h b/src/graphics/directx/DirectXShaderProgram.h new file mode 100644 index 0000000..8e4f4bf --- /dev/null +++ b/src/graphics/directx/DirectXShaderProgram.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include + +using Path = std::filesystem::path; + +class DirectXShaderProgram +{ +public: + DirectXShaderProgram(const Path& vertShaderPath, const Path& pixelShaderPath); + + ID3DBlob* getVertexShader() const; + ID3DBlob* getPixelShader() const; +private: + Microsoft::WRL::ComPtr mVertexShader; + Microsoft::WRL::ComPtr mPixelShader; +}; \ No newline at end of file diff --git a/src/graphics/directx/DirectXTextPainter.cpp b/src/graphics/directx/DirectXTextPainter.cpp new file mode 100644 index 0000000..0c77407 --- /dev/null +++ b/src/graphics/directx/DirectXTextPainter.cpp @@ -0,0 +1,64 @@ +#include "DirectXTextPainter.h" + +#include "DrawingContext.h" +#include "DrawingSurface.h" + +#include "FontsManager.h" +#include "FontGlyph.h" + +#include "DirectXShaderProgram.h" +#include "TextData.h" + +#include "SceneText.h" + +#include "File.h" + +#include + +#include +#include +#include + +DirectXTextPainter::DirectXTextPainter() +{ + +} + + +void DirectXTextPainter::paint(SceneText* text, DrawingContext* context) +{ + +} + +void DirectXTextPainter::paint(ID2D1DeviceContext2* d2dContext, float width, float height) +{ + D2D1_RECT_F textRect = D2D1::RectF(0, 0, width, height); + static const WCHAR text[] = L"11On12"; + + d2dContext->BeginDraw(); + d2dContext->SetTransform(D2D1::Matrix3x2F::Identity()); + d2dContext->DrawText(text, _countof(text) - 1, mTextFormat.Get(), &textRect, mTextBrush.Get()); + d2dContext->EndDraw(); +} + +void DirectXTextPainter::initializeBrush(ID2D1DeviceContext2* d2dContext) +{ + d2dContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &mTextBrush); +} + +void DirectXTextPainter::initializeTextFormat(IDWriteFactory* directWriteFactory) +{ + directWriteFactory->CreateTextFormat( + L"Verdana", + NULL, + DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + 50, + L"en-us", + &mTextFormat + ); + mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); + mTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); +} + diff --git a/src/graphics/directx/DirectXTextPainter.h b/src/graphics/directx/DirectXTextPainter.h new file mode 100644 index 0000000..a2585ea --- /dev/null +++ b/src/graphics/directx/DirectXTextPainter.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +class DrawingContext; +class DirectXShaderProgram; + +class TextData; +class SceneText; + +struct ID2D1DeviceContext2; +struct ID2D1SolidColorBrush; +struct IDWriteFactory; + +class DirectXTextPainter +{ +public: + DirectXTextPainter(); + + void paint(SceneText* text, DrawingContext* context); + + void paint(ID2D1DeviceContext2* d2dContext, float width, float height); + + void initializeBrush(ID2D1DeviceContext2* d2dContext); + + void initializeTextFormat(IDWriteFactory* directWriteFactory); + +private: + Microsoft::WRL::ComPtr mTextBrush; + Microsoft::WRL::ComPtr mTextFormat; +}; diff --git a/src/windows/ui_interfaces/win32/shaders.hlsl b/src/graphics/directx/shaders.hlsl similarity index 100% rename from src/windows/ui_interfaces/win32/shaders.hlsl rename to src/graphics/directx/shaders.hlsl diff --git a/src/ui_elements/desktop_elements/Window.cpp b/src/ui_elements/desktop_elements/Window.cpp index 3082831..c1cbece 100644 --- a/src/ui_elements/desktop_elements/Window.cpp +++ b/src/ui_elements/desktop_elements/Window.cpp @@ -116,6 +116,11 @@ void Window::show() } } +DrawingContext* Window::getDrawingContent() const +{ + return mDrawingContext.get(); +} + void Window::doPaint(mt::Screen* screen) { if (mPlatformWindow) diff --git a/src/ui_elements/desktop_elements/Window.h b/src/ui_elements/desktop_elements/Window.h index bf44a41..a31371b 100644 --- a/src/ui_elements/desktop_elements/Window.h +++ b/src/ui_elements/desktop_elements/Window.h @@ -98,6 +98,8 @@ public: return mParent; } + DrawingContext* getDrawingContent() const; + private: WidgetPtr mWidget {nullptr}; std::string mTitle; diff --git a/src/visual_elements/TextNode.cpp b/src/visual_elements/TextNode.cpp index 5083a9c..b34d289 100644 --- a/src/visual_elements/TextNode.cpp +++ b/src/visual_elements/TextNode.cpp @@ -97,6 +97,11 @@ unsigned TextNode::getNumSceneItems() const void TextNode::updateLines(FontsManager* fontsManager) { + if (!fontsManager) + { + return; + } + auto original_count = mTextData.mLines.size(); std::vector lines = StringUtils::toLines(mTextData.mContent); std::vector output_lines; diff --git a/src/windows/ui_interfaces/win32/Win32DxInterface.cpp b/src/windows/ui_interfaces/win32/Win32DxInterface.cpp index 6cc121c..ef581ce 100644 --- a/src/windows/ui_interfaces/win32/Win32DxInterface.cpp +++ b/src/windows/ui_interfaces/win32/Win32DxInterface.cpp @@ -31,7 +31,6 @@ void Win32DxInterface::getHardwareAdapter(IDXGIAdapter1** ppAdapter) IDXGIAdapter1* pAdapter = nullptr; if (DXGI_ERROR_NOT_FOUND == mFactory->EnumAdapters1(adapterIndex, &pAdapter)) { - // No more adapters to enumerate. break; } @@ -92,50 +91,71 @@ ID3D11DeviceContext* Win32DxInterface::getD3d11DeviceContext() const void Win32DxInterface::initialize() { - if (!DirectX::XMVerifyCPUSupport()) - { - MLOG_ERROR("Directx math not supported"); - return; - } - + MLOG_INFO("Initialize DX"); + UINT dxgiFactoryFlags = 0; #if defined(DEBUG) || defined(_DEBUG) - // Enable the D3D12 debug layer. { - ID3D12Debug* debugController; - D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)); - debugController->EnableDebugLayer(); + Microsoft::WRL::ComPtr debugController; + if (SUCCEEDED(::D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) + { + debugController->EnableDebugLayer(); + dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; + } } #endif - HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory7), (void**)(&mFactory)); + HRESULT hr = ::CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&mFactory)); - IDXGIAdapter1* pAdapter{ nullptr }; + Microsoft::WRL::ComPtr pAdapter; getHardwareAdapter(&pAdapter); - if (pAdapter) { - MLOG_INFO("Found adapter"); - if (SUCCEEDED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice)))) + if (!SUCCEEDED(D3D12CreateDevice(pAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice)))) { - MLOG_INFO("Got device"); + MLOG_ERROR("Failed to create D3D12 device."); + return; } } else { - MLOG_INFO("No adapter found"); + MLOG_ERROR("Failed to get DX hardware adapter."); return; } D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + if (!SUCCEEDED(mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)))) + { + MLOG_ERROR("Failed to create command queue."); + return; + } - mDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)); - - mIsValid = initializeD2d(); + const auto init_2d_ok = initializeD2d(); + mIsValid = init_2d_ok; } bool Win32DxInterface::initializeD2d() +{ + if (!initializeD11on12()) + { + return false; + } + + D2D1_FACTORY_OPTIONS d2dFactoryOptions = {}; + D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &mD2dFactory); + Microsoft::WRL::ComPtr dxgiDevice; + mD3d11On12Device.As(&dxgiDevice); + + mD2dFactory->CreateDevice(dxgiDevice.Get(), &mD2dDevice); + + D2D1_DEVICE_CONTEXT_OPTIONS deviceOptions = D2D1_DEVICE_CONTEXT_OPTIONS_NONE; + mD2dDevice->CreateDeviceContext(deviceOptions, &mD2dDeviceContext); + + return initializeDirectWrite(); +} + +bool Win32DxInterface::initializeD11on12() { Microsoft::WRL::ComPtr d3d11Device; UINT d3d11DeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; @@ -151,24 +171,12 @@ bool Win32DxInterface::initializeD2d() &mD3d11DeviceContext, nullptr ); - d3d11Device.As(&mD3d11On12Device); + return true; +} - // Create D2D/DWrite components. - { - D2D1_DEVICE_CONTEXT_OPTIONS deviceOptions = D2D1_DEVICE_CONTEXT_OPTIONS_NONE; - D2D1_FACTORY_OPTIONS d2dFactoryOptions = {}; - - D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &mD2dFactory); - Microsoft::WRL::ComPtr dxgiDevice; - mD3d11On12Device.As(&dxgiDevice); - - mD2dFactory->CreateDevice(dxgiDevice.Get(), &mD2dDevice); - - mD2dDevice->CreateDeviceContext(deviceOptions, &mD2dDeviceContext); - - DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &mDWriteFactory); - } - +bool Win32DxInterface::initializeDirectWrite() +{ + DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &mDWriteFactory); return true; } \ No newline at end of file diff --git a/src/windows/ui_interfaces/win32/Win32DxInterface.h b/src/windows/ui_interfaces/win32/Win32DxInterface.h index 43db734..ee8a0d6 100644 --- a/src/windows/ui_interfaces/win32/Win32DxInterface.h +++ b/src/windows/ui_interfaces/win32/Win32DxInterface.h @@ -42,6 +42,8 @@ private: void getHardwareAdapter(IDXGIAdapter1** ppAdapter); bool initializeD2d(); + bool initializeD11on12(); + bool initializeDirectWrite(); bool mIsValid{ false }; diff --git a/src/windows/ui_interfaces/win32/Win32DxWindowInterface.cpp b/src/windows/ui_interfaces/win32/Win32DxWindowInterface.cpp index 087a017..141f4a3 100644 --- a/src/windows/ui_interfaces/win32/Win32DxWindowInterface.cpp +++ b/src/windows/ui_interfaces/win32/Win32DxWindowInterface.cpp @@ -4,6 +4,13 @@ #include "Win32Window.h" #include "Window.h" #include "Widget.h" +#include "FileLogger.h" + +#include "DrawingContext.h" +#include "DirectXPainter.h" +#include "DirectXMeshPainter.h" +#include "DirectXTextPainter.h" +#include "DirectXShaderProgram.h" #include #include @@ -14,11 +21,13 @@ #include #include #include +#include #include -Win32DxWindowInterface::Win32DxWindowInterface(Win32DxInterface* dxInterface) - : mDxInterface(dxInterface) +Win32DxWindowInterface::Win32DxWindowInterface(mt::Window* window, Win32DxInterface* dxInterface) + : mWindow(window), + mDxInterface(dxInterface) { } @@ -37,6 +46,11 @@ void Win32DxWindowInterface::destroyWindow() void Win32DxWindowInterface::onRender() { + if (!mIsValid) + { + return; + } + // Record all the commands we need to render the scene into the command list. populateCommandList(); @@ -54,6 +68,8 @@ void Win32DxWindowInterface::onRender() void Win32DxWindowInterface::populateCommandList() { + auto painter = dynamic_cast(mWindow->getDrawingContent()->getPainter()); + // Command list allocators can only be reset when the associated // command lists have finished execution on the GPU; apps should use // fences to determine GPU execution progress. @@ -62,10 +78,10 @@ void Win32DxWindowInterface::populateCommandList() // However, when ExecuteCommandList() is called on a particular command // list, that command list can then be reset at any time and must be before // re-recording. - mCommandList->Reset(mCommandAllocator.Get(), mPipelineState.Get()); + mCommandList->Reset(mCommandAllocator.Get(), painter->getMeshPainter()->getPipelineState()); // Set necessary state. - mCommandList->SetGraphicsRootSignature(mRootSignature.Get()); + mCommandList->SetGraphicsRootSignature(painter->getMeshPainter()->getRootSignature()); mCommandList->RSSetViewports(1, &mViewport); mCommandList->RSSetScissorRects(1, &mScissorRect); @@ -76,12 +92,7 @@ void Win32DxWindowInterface::populateCommandList() CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart(), static_cast(mFrameIndex), mRtvDescriptorSize); mCommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); - // Record commands. - const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; - mCommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); - mCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - mCommandList->IASetVertexBuffers(0, 1, &mVertexBufferView); - mCommandList->DrawInstanced(3, 1, 0, 0); + painter->getMeshPainter()->updateCommandList(rtvHandle, mCommandList.Get()); // Indicate that the back buffer will now be used to present. //barrier = CD3DX12_RESOURCE_BARRIER::Transition(mRenderTargets[mFrameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); @@ -90,276 +101,17 @@ void Win32DxWindowInterface::populateCommandList() mCommandList->Close(); } -bool Win32DxWindowInterface::initialize(mt::Window* window) -{ - if (mInitialized) - { - return true; - } - - const auto width = window->getWidth(); - const auto height = window->getHeight(); - mViewport = CD3DX12_VIEWPORT(0.0f, 0.0f, width, height); - mScissorRect = CD3DX12_RECT(0, 0, width, height); - - loadPipeline(window); - - loadAssets(); - - return true; -} - -void Win32DxWindowInterface::loadPipeline(mt::Window* window) -{ - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; - swapChainDesc.BufferCount = FrameCount; - - const auto width = window->getWidth(); - const auto height = window->getHeight(); - swapChainDesc.Width = width; - swapChainDesc.Height = height; - - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swapChainDesc.SampleDesc.Count = 1; - - const auto hwnd = dynamic_cast(window->getPlatformWindow())->getHandle(); - - Microsoft::WRL::ComPtr swapChain; - mDxInterface->getFactory()->CreateSwapChainForHwnd( - mDxInterface->getCommandQueue(), - hwnd, - &swapChainDesc, - nullptr, - nullptr, - &swapChain - ); - mDxInterface->getFactory()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); - swapChain.As(&mSwapChain); - auto mFrameIndex = mSwapChain->GetCurrentBackBufferIndex(); - - // Query the desktop's dpi settings, which will be used to create - // D2D's render targets. - float dpiX{ 0.0 }; - float dpiY{ 0.0 }; - mDxInterface->getD2dFactory()->GetDesktopDpi(&dpiX, &dpiY); - - D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( - D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), - dpiX, - dpiY - ); - - // Create descriptor heaps. - { - // Describe and create a render target view (RTV) descriptor heap. - D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; - rtvHeapDesc.NumDescriptors = FrameCount; - rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - mDxInterface->getDevice()->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&mRtvHeap)); - - mRtvDescriptorSize = mDxInterface->getDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - } - - // Create frame resources. - { - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart()); - - // Create a RTV for each frame. - for (UINT n = 0; n < FrameCount; n++) - { - mSwapChain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n])); - mDxInterface->getDevice()->CreateRenderTargetView(mRenderTargets[n].Get(), nullptr, rtvHandle); - - D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; - mDxInterface->get11On12Device()->CreateWrappedResource( - mRenderTargets[n].Get(), - &d3d11Flags, - D3D12_RESOURCE_STATE_RENDER_TARGET, - D3D12_RESOURCE_STATE_PRESENT, - IID_PPV_ARGS(&mWrappedBackBuffers[n]) - ); - - // Create a render target for D2D to draw directly to this back buffer. - Microsoft::WRL::ComPtr surface; - mWrappedBackBuffers[n].As(&surface); - mDxInterface->getD2dContext()->CreateBitmapFromDxgiSurface( - surface.Get(), - &bitmapProperties, - &mD2dRenderTargets[n] - ); - - rtvHandle.Offset(1, mRtvDescriptorSize); - } - } - - mDxInterface->getDevice()->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator)); -} - -void Win32DxWindowInterface::loadAssets() -{ - // Create an empty root signature. - { - CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc; - rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); - - Microsoft::WRL::ComPtr signature; - Microsoft::WRL::ComPtr error; - D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error); - mDxInterface->getDevice()->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)); - } - - // Create the pipeline state, which includes compiling and loading shaders. - { - Microsoft::WRL::ComPtr vertexShader; - Microsoft::WRL::ComPtr pixelShader; - -#if defined(_DEBUG) - // Enable better shader debugging with the graphics debugging tools. - // UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; - UINT compileFlags = 0; -#else - UINT compileFlags = 0; -#endif - - auto shader_path = std::filesystem::path(__FILE__).parent_path() / "shaders.hlsl"; - - D3DCompileFromFile(shader_path.c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr); - D3DCompileFromFile(shader_path.c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr); - - // Define the vertex input layout. - D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } - }; - - // Describe and create the graphics pipeline state object (PSO). - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; - psoDesc.pRootSignature = mRootSignature.Get(); - psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); - psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); - psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); - psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); - psoDesc.DepthStencilState.DepthEnable = FALSE; - psoDesc.DepthStencilState.StencilEnable = FALSE; - psoDesc.SampleMask = UINT_MAX; - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; - psoDesc.SampleDesc.Count = 1; - mDxInterface->getDevice()->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPipelineState)); - } - - // Create the command list. - mDxInterface->getDevice()->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), mPipelineState.Get(), IID_PPV_ARGS(&mCommandList)); - - // Create D2D/DWrite objects for rendering text. - { - mDxInterface->getD2dContext()->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &mTextBrush); - mDxInterface->getDirectWriteFactory()->CreateTextFormat( - L"Verdana", - NULL, - DWRITE_FONT_WEIGHT_NORMAL, - DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - 50, - L"en-us", - &mTextFormat - ); - mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); - mTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); - } - - - // Command lists are created in the recording state, but there is nothing - // to record yet. The main loop expects it to be closed, so close it now. - mCommandList->Close(); - - // Create the vertex buffer. - { - // Define the geometry for a triangle. - const float aspectRatio = 1.0; - Vertex triangleVertices[] = - { - { { 0.0f, 0.25f * aspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, - { { 0.25f, -0.25f * aspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, - { { -0.25f, -0.25f * aspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } - }; - - const UINT vertexBufferSize = sizeof(triangleVertices); - - // Note: using upload heaps to transfer static data like vert buffers is not - // recommended. Every time the GPU needs it, the upload heap will be marshalled - // over. Please read up on Default Heap usage. An upload heap is used here for - // code simplicity and because there are very few verts to actually transfer. - CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD); - auto desc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize); - mDxInterface->getDevice()->CreateCommittedResource( - &heapProps, - D3D12_HEAP_FLAG_NONE, - &desc, - D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, - IID_PPV_ARGS(&mVertexBuffer)); - - // Copy the triangle data to the vertex buffer. - UINT8* pVertexDataBegin; - CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. - mVertexBuffer->Map(0, &readRange, reinterpret_cast(&pVertexDataBegin)); - memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices)); - mVertexBuffer->Unmap(0, nullptr); - - // Initialize the vertex buffer view. - mVertexBufferView.BufferLocation = mVertexBuffer->GetGPUVirtualAddress(); - mVertexBufferView.StrideInBytes = sizeof(Vertex); - mVertexBufferView.SizeInBytes = vertexBufferSize; - } - - // Create synchronization objects and wait until assets have been uploaded to the GPU. - { - mDxInterface->getDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)); - mFenceValue = 1; - - // Create an event handle to use for frame synchronization. - mFenceEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (mFenceEvent == nullptr) - { - //ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); - } - - // Wait for the command list to execute; we are reusing the same command - // list in our main loop but for now, we just want to wait for setup to - // complete before continuing. - waitForPreviousFrame(); - } -} - void Win32DxWindowInterface::renderD2d() { - D2D1_SIZE_F rtSize = mD2dRenderTargets[mFrameIndex]->GetSize(); - D2D1_RECT_F textRect = D2D1::RectF(0, 0, rtSize.width, rtSize.height); - static const WCHAR text[] = L"11On12"; - // Acquire our wrapped render target resource for the current back buffer. mDxInterface->get11On12Device()->AcquireWrappedResources(mWrappedBackBuffers[mFrameIndex].GetAddressOf(), 1); // Render text directly to the back buffer. mDxInterface->getD2dContext()->SetTarget(mD2dRenderTargets[mFrameIndex].Get()); - mDxInterface->getD2dContext()->BeginDraw(); - mDxInterface->getD2dContext()->SetTransform(D2D1::Matrix3x2F::Identity()); - mDxInterface->getD2dContext()->DrawText( - text, - _countof(text) - 1, - mTextFormat.Get(), - &textRect, - mTextBrush.Get() - ); - mDxInterface->getD2dContext()->EndDraw(); + + auto painter = dynamic_cast(mWindow->getDrawingContent()->getPainter()); + D2D1_SIZE_F rtSize = mD2dRenderTargets[mFrameIndex]->GetSize(); + painter->getTextPainter()->paint(mDxInterface->getD2dContext(), rtSize.width, rtSize.height); // Release our wrapped render target resource. Releasing // transitions the back buffer resource to the state specified @@ -370,6 +122,177 @@ void Win32DxWindowInterface::renderD2d() mDxInterface->getD3d11DeviceContext()->Flush(); } +bool Win32DxWindowInterface::initialize() +{ + if (mInitialized) + { + return true; + } + mInitialized = true; + + const auto width = mWindow->getWidth(); + const auto height = mWindow->getHeight(); + mViewport = CD3DX12_VIEWPORT(0.0f, 0.0f, static_cast(width), static_cast(height)); + mScissorRect = CD3DX12_RECT(0, 0, width, height); + + if (!loadPipeline()) + { + return false; + } + + if (!loadAssets()) + { + return false; + } + + mIsValid = true; + return true; +} + +bool Win32DxWindowInterface::loadPipeline() +{ + if (!setupSwapChain()) + { + return false; + } + + setupDescriptorHeaps(); + + setupFrameResources(); + + mDxInterface->getDevice()->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocator)); + + return true; +} + +bool Win32DxWindowInterface::setupSwapChain() +{ + const auto width = mWindow->getWidth(); + const auto height = mWindow->getHeight(); + const auto hwnd = dynamic_cast(mWindow->getPlatformWindow())->getHandle(); + + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.BufferCount = FrameCount; + swapChainDesc.Width = width; + swapChainDesc.Height = height; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.SampleDesc.Count = 1; + + auto command_queue = mDxInterface->getCommandQueue(); + Microsoft::WRL::ComPtr swapChain; + const auto hr = mDxInterface->getFactory()->CreateSwapChainForHwnd(command_queue, hwnd, &swapChainDesc, nullptr, nullptr, &swapChain); + if (!SUCCEEDED(hr)) + { + MLOG_ERROR("Failed to create swap chain for hwnd: " << hwnd); + return false; + } + swapChain.As(&mSwapChain); + + // Prevent fullscreen toggle + mDxInterface->getFactory()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); + + // Update index for backbuffer + mFrameIndex = mSwapChain->GetCurrentBackBufferIndex(); + return true; +} + +void Win32DxWindowInterface::setupDescriptorHeaps() +{ + D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; + rtvHeapDesc.NumDescriptors = FrameCount; + rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + mDxInterface->getDevice()->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&mRtvHeap)); + mRtvDescriptorSize = mDxInterface->getDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); +} + +void Win32DxWindowInterface::setupFrameResources() +{ + CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart()); + + float dpiX{ 0.0 }; + float dpiY{ 0.0 }; + mDxInterface->getD2dFactory()->GetDesktopDpi(&dpiX, &dpiY); + D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), + dpiX, + dpiY + ); + + for (UINT n = 0; n < FrameCount; n++) + { + setupFrameResource(n, rtvHandle, bitmapProperties); + } +} + +void Win32DxWindowInterface::setupFrameResource(UINT idx, CD3DX12_CPU_DESCRIPTOR_HANDLE& descriptorHandle, const D2D1_BITMAP_PROPERTIES1& bitmapProps) +{ + mSwapChain->GetBuffer(idx, IID_PPV_ARGS(&mRenderTargets[idx])); + mDxInterface->getDevice()->CreateRenderTargetView(mRenderTargets[idx].Get(), nullptr, descriptorHandle); + + setupFrameD2DResource(idx, bitmapProps); + + descriptorHandle.Offset(1, mRtvDescriptorSize); +} + +void Win32DxWindowInterface::setupFrameD2DResource(UINT idx, const D2D1_BITMAP_PROPERTIES1& bitmapProps) +{ + D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; + mDxInterface->get11On12Device()->CreateWrappedResource(mRenderTargets[idx].Get(),&d3d11Flags, + D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&mWrappedBackBuffers[idx])); + + Microsoft::WRL::ComPtr surface; + mWrappedBackBuffers[idx].As(&surface); + mDxInterface->getD2dContext()->CreateBitmapFromDxgiSurface(surface.Get(), &bitmapProps, &mD2dRenderTargets[idx]); +} + +bool Win32DxWindowInterface::loadAssets() +{ + auto painter = dynamic_cast(mWindow->getDrawingContent()->getPainter()); + + painter->getMeshPainter()->createRootSignature(mDxInterface->getDevice()); + painter->getMeshPainter()->createPipelineStateObject(mDxInterface->getDevice()); + + // Create the command list. + mDxInterface->getDevice()->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocator.Get(), + painter->getMeshPainter()->getPipelineState(), IID_PPV_ARGS(&mCommandList)); + + // Create D2D/DWrite objects for rendering text. + painter->getTextPainter()->initializeBrush(mDxInterface->getD2dContext()); + painter->getTextPainter()->initializeTextFormat(mDxInterface->getDirectWriteFactory()); + + // Command lists are created in the recording state, but there is nothing + // to record yet. The main loop expects it to be closed, so close it now. + mCommandList->Close(); + + painter->getMeshPainter()->createVertexBuffer(mDxInterface->getDevice()); + + createSyncObjects(); + + return true; +} + +void Win32DxWindowInterface::createSyncObjects() +{ + mDxInterface->getDevice()->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)); + mFenceValue = 1; + + // Create an event handle to use for frame synchronization. + mFenceEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (mFenceEvent == nullptr) + { + //ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); + } + + // Wait for the command list to execute; we are reusing the same command + // list in our main loop but for now, we just want to wait for setup to + // complete before continuing. + waitForPreviousFrame(); +} + void Win32DxWindowInterface::waitForPreviousFrame() { // Signal and increment the fence value. diff --git a/src/windows/ui_interfaces/win32/Win32DxWindowInterface.h b/src/windows/ui_interfaces/win32/Win32DxWindowInterface.h index 8297284..7d598f6 100644 --- a/src/windows/ui_interfaces/win32/Win32DxWindowInterface.h +++ b/src/windows/ui_interfaces/win32/Win32DxWindowInterface.h @@ -4,7 +4,6 @@ #include #include -#include #include class Win32DxInterface; @@ -25,27 +24,23 @@ struct ID3D12PipelineState; struct ID3D11Resource; struct ID2D1Bitmap1; -struct ID2D1SolidColorBrush; -struct IDWriteTextFormat; +struct D2D1_BITMAP_PROPERTIES1; +struct CD3DX12_CPU_DESCRIPTOR_HANDLE; class Win32DxWindowInterface { public: - Win32DxWindowInterface(Win32DxInterface* dxInterface); + Win32DxWindowInterface(mt::Window* window, Win32DxInterface* dxInterface); ~Win32DxWindowInterface(); - bool initialize(mt::Window* window); + bool initialize(); void onRender(); - //void afterPaint(); - - //void resizeViewPort(unsigned width, unsigned height); - private: - void loadPipeline(mt::Window* window); - void loadAssets(); + bool loadPipeline(); + bool loadAssets(); void populateCommandList(); void waitForPreviousFrame(); @@ -53,16 +48,21 @@ private: void renderD2d(); + bool setupSwapChain(); + void setupDescriptorHeaps(); + + void setupFrameResources(); + void setupFrameResource(UINT idx, CD3DX12_CPU_DESCRIPTOR_HANDLE& descriptorHandle, const D2D1_BITMAP_PROPERTIES1& bitmapProps); + void setupFrameD2DResource(UINT idx, const D2D1_BITMAP_PROPERTIES1& bitmapProps); + + void createSyncObjects(); + static const UINT FrameCount = 2; Win32DxInterface* mDxInterface{ nullptr }; bool mInitialized{ false }; + bool mIsValid{ false }; - struct Vertex - { - DirectX::XMFLOAT3 position; - DirectX::XMFLOAT4 color; - }; - + mt::Window* mWindow{ nullptr }; CD3DX12_VIEWPORT mViewport; CD3DX12_RECT mScissorRect; @@ -71,9 +71,6 @@ private: Microsoft::WRL::ComPtr mSwapChain; - Microsoft::WRL::ComPtr mRootSignature; - Microsoft::WRL::ComPtr mPipelineState; - Microsoft::WRL::ComPtr mRtvHeap; UINT mRtvDescriptorSize{ 0 }; @@ -81,12 +78,6 @@ private: Microsoft::WRL::ComPtr mWrappedBackBuffers[FrameCount]; Microsoft::WRL::ComPtr mD2dRenderTargets[FrameCount]; - Microsoft::WRL::ComPtr mTextBrush; - Microsoft::WRL::ComPtr mTextFormat; - - Microsoft::WRL::ComPtr mVertexBuffer; - D3D12_VERTEX_BUFFER_VIEW mVertexBufferView{}; - Microsoft::WRL::ComPtr mFence; uint64_t mFenceValue = 0; uint64_t mFrameFenceValues[FrameCount] = {}; diff --git a/src/windows/ui_interfaces/win32/Win32Window.cpp b/src/windows/ui_interfaces/win32/Win32Window.cpp index f9ef4b2..252e04c 100644 --- a/src/windows/ui_interfaces/win32/Win32Window.cpp +++ b/src/windows/ui_interfaces/win32/Win32Window.cpp @@ -20,7 +20,7 @@ Win32Window::Win32Window(mt::Window* window, Win32DxInterface* dxInterface) { if (dxInterface) { - mDxInterface = std::make_unique(dxInterface); + mDxInterface = std::make_unique(window, dxInterface); } } @@ -80,6 +80,13 @@ void Win32Window::createNative(Win32ApplicationContext* context, DesktopManager* MLOG_INFO("Request window create got handle: " << "0x" << mHandle); } +void Win32Window::onPaintMessage() +{ + mDesktopManager->onUiEvent(PaintEvent::Create()); + + mWindow->doPaint(nullptr); +} + LRESULT CALLBACK Win32Window::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) @@ -89,7 +96,6 @@ LRESULT CALLBACK Win32Window::WindowProc(UINT message, WPARAM wParam, LPARAM lPa PostQuitMessage(0); return 0; } - case WM_CHAR: { auto key_event = std::make_unique(); @@ -99,25 +105,9 @@ LRESULT CALLBACK Win32Window::WindowProc(UINT message, WPARAM wParam, LPARAM lPa key_event->setKeyString(StringUtils::convert(std::wstring(1, keyChar))); mDesktopManager->onUiEvent(std::move(key_event)); } - case WM_PAINT: { - if (mDxInterface) - { - mDxInterface->onRender(); - } - else - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(mHandle, &ps); - - FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1)); - - auto text = L"Hello World"; - auto val = DrawText(hdc, (LPCSTR)(&text[0]), -1, &ps.rcPaint, 0); - - EndPaint(mHandle, &ps); - } + onPaintMessage(); } } return DefWindowProc(mHandle, message, wParam, lParam); @@ -136,7 +126,9 @@ static LRESULT CALLBACK FreeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPAR if (::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window) == 0) { if (GetLastError() != 0) + { return FALSE; + } } } else @@ -157,6 +149,26 @@ void Win32Window::beforePaint(mt::Screen* screen) { if (mDxInterface) { - mDxInterface->initialize(mWindow); + mDxInterface->initialize(); + } +} + +void Win32Window::afterPaint(mt::Screen* screen) +{ + if (mDxInterface) + { + mDxInterface->onRender(); + } + else + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(mHandle, &ps); + + FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1)); + + auto text = L"Hello World"; + auto val = DrawText(hdc, (LPCSTR)(&text[0]), -1, &ps.rcPaint, 0); + + EndPaint(mHandle, &ps); } } \ No newline at end of file diff --git a/src/windows/ui_interfaces/win32/Win32Window.h b/src/windows/ui_interfaces/win32/Win32Window.h index b160d2e..9559180 100644 --- a/src/windows/ui_interfaces/win32/Win32Window.h +++ b/src/windows/ui_interfaces/win32/Win32Window.h @@ -47,12 +47,11 @@ public: void beforePaint(mt::Screen* screen); - void afterPaint(mt::Screen* screen) - { - - } + void afterPaint(mt::Screen* screen); private: + void onPaintMessage(); + HWND mHandle{ 0 }; int mCmdShow{ 0 }; DesktopManager* mDesktopManager{ nullptr };