generated from McMassiveNZ/opengl-starter
Following along with the example for the simple
triangle in the DirectX-Graphics-Samples repository it can render a simple triangle
This commit is contained in:
@@ -2,3 +2,5 @@
|
||||
|
||||
An dx12-starter cpp project which can open a simple window, create a dx12 device with associated resources and render a triangle to the screen. The project contains boilerplate for CMake, testing and basic CI. Static Analysis, Unit Tests and Sanitizers are off by default
|
||||
|
||||
References
|
||||
* https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12HelloWorld/src
|
||||
4094
external/d3dx12.h
vendored
Normal file
4094
external/d3dx12.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
target_sources(
|
||||
dx12-starter PRIVATE
|
||||
device.h
|
||||
renderer.h
|
||||
window.h
|
||||
)
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace dx12_starter
|
||||
{
|
||||
|
||||
struct DeviceCreateParams
|
||||
{
|
||||
void* nativeWindowHandle;
|
||||
int versionMajor;
|
||||
int versionMinor;
|
||||
};
|
||||
|
||||
class IDevice
|
||||
{
|
||||
public:
|
||||
virtual ~IDevice() = default;
|
||||
|
||||
virtual void ClearBuffers() = 0;
|
||||
virtual void Present() = 0;
|
||||
virtual void DrawScene() = 0;
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
static std::unique_ptr<IDevice> Create(const DeviceCreateParams& params);
|
||||
};
|
||||
|
||||
} // namespace dx12_starter
|
||||
33
include/renderer.h
Normal file
33
include/renderer.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace dx12_starter
|
||||
{
|
||||
struct APIVersion
|
||||
{
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
};
|
||||
|
||||
struct RendererCreateParams
|
||||
{
|
||||
void* nativeWindowHandle;
|
||||
APIVersion minimumVersion;
|
||||
uint32_t frameCount;
|
||||
};
|
||||
|
||||
class IRenderer
|
||||
{
|
||||
public:
|
||||
virtual ~IRenderer() = default;
|
||||
|
||||
virtual void RecordCommands() = 0;
|
||||
virtual void Render() = 0;
|
||||
virtual void WaitForPreviousFrame() = 0;
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
static std::unique_ptr<IRenderer> Create(const RendererCreateParams& params);
|
||||
};
|
||||
|
||||
} // namespace dx12_starter
|
||||
@@ -7,16 +7,25 @@ add_executable(
|
||||
target_sources(${current_target} PRIVATE
|
||||
pch.h
|
||||
main.cpp
|
||||
dx12_device.cpp
|
||||
dx12_renderer.cpp
|
||||
win32_window.cpp
|
||||
dx12_starter_dxgi.h
|
||||
dx12_starter_dxgi.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${current_target} PUBLIC .)
|
||||
target_include_directories(${current_target} PUBLIC . ${CMAKE_SOURCE_DIR}/external)
|
||||
target_precompile_headers(${current_target} PRIVATE pch.h pch.cpp)
|
||||
target_compile_definitions(${current_target} PRIVATE SHADERS_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
if(MSVC)
|
||||
target_sources(${current_target} PRIVATE pch.cpp)
|
||||
target_link_libraries(${current_target} PUBLIC d3d12.lib dxgi.lib)
|
||||
target_link_libraries(${current_target} PRIVATE
|
||||
d3d12.lib
|
||||
dxgi.lib
|
||||
dxguid.lib
|
||||
d3dcompiler.lib
|
||||
runtimeobject.lib
|
||||
)
|
||||
endif()
|
||||
|
||||
if( ENABLE_ALL_REASONABLE_WARNINGS )
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace dx12_starter
|
||||
{
|
||||
|
||||
struct Win32DX12Impl final : public IDevice
|
||||
{
|
||||
void ClearBuffers();
|
||||
void Present();
|
||||
void DrawScene();
|
||||
void Destroy();
|
||||
|
||||
HWND m_windowHandle;
|
||||
};
|
||||
|
||||
void Win32DX12Impl::ClearBuffers()
|
||||
{
|
||||
}
|
||||
|
||||
void Win32DX12Impl::Present()
|
||||
{
|
||||
}
|
||||
|
||||
void Win32DX12Impl::DrawScene()
|
||||
{
|
||||
}
|
||||
|
||||
void Win32DX12Impl::Destroy()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<IDevice> IDevice::Create([[maybe_unused]] const DeviceCreateParams& params)
|
||||
{
|
||||
return std::make_unique<Win32DX12Impl>();
|
||||
}
|
||||
} // namespace ogl_starter
|
||||
529
src/dx12_renderer.cpp
Normal file
529
src/dx12_renderer.cpp
Normal file
@@ -0,0 +1,529 @@
|
||||
#include "pch.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include "dx12_starter_dxgi.h"
|
||||
|
||||
namespace dx12_starter
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static constexpr UINT s_MaxBackBufferCount = 2;
|
||||
|
||||
auto ParseAPIVersion(APIVersion version) -> D3D_FEATURE_LEVEL
|
||||
{
|
||||
if (version.major < 9)
|
||||
{
|
||||
return D3D_FEATURE_LEVEL_1_0_CORE;
|
||||
}
|
||||
if (version.major == 9)
|
||||
{
|
||||
if (version.minor == 1)
|
||||
return D3D_FEATURE_LEVEL_9_1;
|
||||
else if (version.minor == 2)
|
||||
return D3D_FEATURE_LEVEL_9_2;
|
||||
else if (version.minor == 3)
|
||||
return D3D_FEATURE_LEVEL_9_3;
|
||||
|
||||
return D3D_FEATURE_LEVEL_9_1;
|
||||
}
|
||||
else if (version.major == 10)
|
||||
{
|
||||
if (version.minor == 0)
|
||||
return D3D_FEATURE_LEVEL_10_0;
|
||||
else if (version.minor == 1)
|
||||
return D3D_FEATURE_LEVEL_10_0;
|
||||
|
||||
return D3D_FEATURE_LEVEL_10_1;
|
||||
}
|
||||
else if (version.major == 11)
|
||||
{
|
||||
if (version.minor == 0)
|
||||
return D3D_FEATURE_LEVEL_11_0;
|
||||
else if (version.minor == 1)
|
||||
return D3D_FEATURE_LEVEL_11_1;
|
||||
|
||||
return D3D_FEATURE_LEVEL_11_0;
|
||||
}
|
||||
else if (version.major == 12)
|
||||
{
|
||||
if (version.minor == 0)
|
||||
return D3D_FEATURE_LEVEL_12_0;
|
||||
else if (version.minor == 1)
|
||||
return D3D_FEATURE_LEVEL_12_1;
|
||||
else if (version.minor == 2)
|
||||
return D3D_FEATURE_LEVEL_12_2;
|
||||
|
||||
return D3D_FEATURE_LEVEL_12_0;
|
||||
}
|
||||
|
||||
return D3D_FEATURE_LEVEL_1_0_CORE;
|
||||
}
|
||||
|
||||
auto EnableDeviceDebugLayer(ComPtr<ID3D12Device> device)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
ComPtr<ID3D12InfoQueue> d3dInfoQueue{};
|
||||
if (SUCCEEDED(device.As(&d3dInfoQueue)))
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
d3dInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
|
||||
d3dInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
|
||||
#endif
|
||||
D3D12_MESSAGE_ID hide[] =
|
||||
{
|
||||
D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE,
|
||||
D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE,
|
||||
|
||||
// Workarounds for debug layer issues on hybrid-graphics systems
|
||||
D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE,
|
||||
D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE};
|
||||
|
||||
D3D12_INFO_QUEUE_FILTER filter = {};
|
||||
filter.DenyList.NumIDs = _countof(hide);
|
||||
filter.DenyList.pIDList = hide;
|
||||
d3dInfoQueue->AddStorageFilterEntries(&filter);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct RendererDX12 final : public IRenderer
|
||||
{
|
||||
RendererDX12() = default;
|
||||
|
||||
void RecordCommands();
|
||||
void Render();
|
||||
void WaitForPreviousFrame();
|
||||
void Destroy();
|
||||
|
||||
HWND m_windowHandle = nullptr;
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
float position[3];
|
||||
float color[4];
|
||||
};
|
||||
|
||||
// Pipeline objects.
|
||||
CD3DX12_VIEWPORT m_viewport = {};
|
||||
CD3DX12_RECT m_scissorRect = {};
|
||||
ComPtr<IDXGISwapChain3> m_swapChain = nullptr;
|
||||
ComPtr<ID3D12Device> m_device = nullptr;
|
||||
ComPtr<ID3D12Resource> m_renderTargets[s_MaxBackBufferCount] = {};
|
||||
ComPtr<ID3D12CommandAllocator> m_commandAllocator = nullptr;
|
||||
ComPtr<ID3D12CommandQueue> m_commandQueue = nullptr;
|
||||
ComPtr<ID3D12RootSignature> m_rootSignature = nullptr;
|
||||
ComPtr<ID3D12DescriptorHeap> m_rtvHeap = nullptr;
|
||||
ComPtr<ID3D12PipelineState> m_pipelineState = nullptr;
|
||||
ComPtr<ID3D12GraphicsCommandList> m_commandList = nullptr;
|
||||
UINT m_rtvDescriptorSize = 0u;
|
||||
|
||||
// App resources.
|
||||
ComPtr<ID3D12Resource> m_vertexBuffer = nullptr;
|
||||
D3D12_VERTEX_BUFFER_VIEW m_vertexBufferView = {};
|
||||
|
||||
// Synchronization objects.
|
||||
UINT m_frameIndex = 0u;
|
||||
HANDLE m_fenceEvent = nullptr;
|
||||
ComPtr<ID3D12Fence> m_fence = nullptr;
|
||||
UINT64 m_fenceValue = 0llu;
|
||||
};
|
||||
|
||||
void RendererDX12::RecordCommands()
|
||||
{
|
||||
// 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.
|
||||
if (FAILED(m_commandAllocator->Reset()))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to reset command allocator", "Command Allocator Failure", MB_ICONERROR | MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (FAILED(m_commandList->Reset(m_commandAllocator.Get(), m_pipelineState.Get())))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to reset command list", "Command Allocator Failure", MB_ICONERROR | MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set necessary state.
|
||||
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
|
||||
m_commandList->RSSetViewports(1, &m_viewport);
|
||||
m_commandList->RSSetScissorRects(1, &m_scissorRect);
|
||||
|
||||
// Indicate that the back buffer will be used as a render target.
|
||||
{
|
||||
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
|
||||
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
|
||||
|
||||
// Record commands.
|
||||
const float clearColor[] = {0.0f, 0.2f, 0.4f, 1.0f};
|
||||
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
|
||||
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
|
||||
m_commandList->DrawInstanced(3, 1, 0, 0);
|
||||
|
||||
// Indicate that the back buffer will now be used to present.
|
||||
{
|
||||
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
if (FAILED(m_commandList->Close()))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to close command list", "Command List Failure", MB_ICONERROR | MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void RendererDX12::Render()
|
||||
{
|
||||
// Execute the command list.
|
||||
ID3D12CommandList* ppCommandLists[] = {m_commandList.Get()};
|
||||
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
|
||||
|
||||
// Present the frame.
|
||||
if (FAILED(m_swapChain->Present(1, 0)))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to preset frame", "Swapchain Failure", MB_ICONERROR | MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void RendererDX12::Destroy()
|
||||
{
|
||||
WaitForPreviousFrame();
|
||||
CloseHandle(m_fenceEvent);
|
||||
}
|
||||
|
||||
void RendererDX12::WaitForPreviousFrame()
|
||||
{
|
||||
// WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
|
||||
// This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
|
||||
// sample illustrates how to use fences for efficient resource usage and to
|
||||
// maximize GPU utilization.
|
||||
|
||||
// Signal and increment the fence value.
|
||||
const UINT64 fence = std::exchange(m_fenceValue, m_fenceValue + 1);
|
||||
if (FAILED(m_commandQueue->Signal(m_fence.Get(), fence)))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the previous frame is finished.
|
||||
if (m_fence->GetCompletedValue() < fence)
|
||||
{
|
||||
if (FAILED(m_fence->SetEventOnCompletion(fence, m_fenceEvent)))
|
||||
{
|
||||
MessageBox(m_windowHandle, "Failed to signal command queue fence", "Command Queue Failure", MB_ICONERROR | MB_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
WaitForSingleObject(m_fenceEvent, INFINITE);
|
||||
}
|
||||
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
std::unique_ptr<IRenderer> IRenderer::Create(const RendererCreateParams& params)
|
||||
{
|
||||
/*
|
||||
- Enable DIGI Debug Layers (optional)
|
||||
- Create DXGI Factory
|
||||
- Create Device
|
||||
- Create Command Queue
|
||||
*/
|
||||
|
||||
auto renderer = std::make_unique<RendererDX12>();
|
||||
|
||||
UINT dxgiFactoryFlags{};
|
||||
#if (_DEBUG)
|
||||
dxgi::EnableDebugLayer();
|
||||
dxgiFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
|
||||
#endif
|
||||
|
||||
auto hWnd = static_cast<HWND>(params.nativeWindowHandle);
|
||||
auto minimumFeatureLevel = ParseAPIVersion(params.minimumVersion);
|
||||
|
||||
renderer->m_windowHandle = hWnd;
|
||||
|
||||
ComPtr<IDXGIFactory4> factory{};
|
||||
if (FAILED(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create DXGIFactory", "DXGI Factory Create Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ComPtr<IDXGIAdapter1> adapter = dxgi::CreateAdapter(factory);
|
||||
if (FAILED(D3D12CreateDevice(adapter.Get(),
|
||||
minimumFeatureLevel,
|
||||
IID_PPV_ARGS(renderer->m_device.ReleaseAndGetAddressOf()))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create D3D12Device", "Device Create Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EnableDeviceDebugLayer(renderer->m_device);
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {
|
||||
.Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
.Priority = 0,
|
||||
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
|
||||
.NodeMask = 0};
|
||||
|
||||
if (FAILED(renderer->m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&renderer->m_commandQueue))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create D3D12CommandQueue", "Command Queue Create Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RECT windowRect{};
|
||||
GetClientRect(hWnd, &windowRect);
|
||||
const UINT width = windowRect.right - windowRect.left;
|
||||
const UINT height = windowRect.bottom - windowRect.top;
|
||||
const float aspectRatio = static_cast<float>(width) / static_cast<float>(height);
|
||||
|
||||
renderer->m_viewport = CD3DX12_VIEWPORT{
|
||||
static_cast<float>(windowRect.left),
|
||||
static_cast<float>(windowRect.top),
|
||||
static_cast<float>(width),
|
||||
static_cast<float>(height)};
|
||||
|
||||
renderer->m_scissorRect = CD3DX12_RECT{
|
||||
windowRect.left,
|
||||
windowRect.top,
|
||||
windowRect.right,
|
||||
windowRect.bottom};
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {
|
||||
.Width = width,
|
||||
.Height = height,
|
||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
.Stereo = FALSE,
|
||||
.SampleDesc = {
|
||||
.Count = 1,
|
||||
.Quality = 0},
|
||||
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
.BufferCount = params.frameCount,
|
||||
.Scaling = DXGI_SCALING_STRETCH,
|
||||
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
|
||||
.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED,
|
||||
.Flags = 0};
|
||||
|
||||
if (ComPtr<IDXGISwapChain1> swapChain{}; FAILED(factory->CreateSwapChainForHwnd(renderer->m_commandQueue.Get(), hWnd, &swapChainDesc, nullptr, nullptr, &swapChain)))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create DXGISwapChain", "Swap Chain Create Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FAILED(swapChain.As(&renderer->m_swapChain)))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create DXGISwapChain3", "Swap Chain Create Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (FAILED(factory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER)))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to Associate DXGI Factory with HWND", "DXGIFactory Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {
|
||||
.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
|
||||
.NumDescriptors = params.frameCount,
|
||||
.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
|
||||
.NodeMask = 0u};
|
||||
|
||||
if (FAILED(renderer->m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&renderer->m_rtvHeap))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create D3D12DescriptorHeap", "Device Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->m_rtvDescriptorSize = renderer->m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle{renderer->m_rtvHeap->GetCPUDescriptorHandleForHeapStart()};
|
||||
for (uint32_t i = 0; i < params.frameCount; ++i)
|
||||
{
|
||||
if (FAILED(renderer->m_swapChain->GetBuffer(i, IID_PPV_ARGS(&renderer->m_renderTargets[i]))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create Render Target", "Device Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->m_device->CreateRenderTargetView(renderer->m_renderTargets[i].Get(), nullptr, rtvHandle);
|
||||
rtvHandle.Offset(1, renderer->m_rtvDescriptorSize);
|
||||
}
|
||||
|
||||
if (FAILED(renderer->m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&renderer->m_commandAllocator))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create Command Allocator", "Device Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
ComPtr<ID3DBlob> signature;
|
||||
ComPtr<ID3DBlob> error;
|
||||
if (FAILED(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Serialize Root Signature", "Root Signature Failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
else if (FAILED(renderer->m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&renderer->m_rootSignature))))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Create Root Signature", "Device Failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the pipeline state, which includes compiling and loading shaders.
|
||||
ComPtr<ID3DBlob> vertexShader{};
|
||||
ComPtr<ID3DBlob> pixelShader{};
|
||||
|
||||
UINT compileFlags = 0;
|
||||
#if defined(_DEBUG)
|
||||
// Enable better shader debugging with the graphics debugging tools.
|
||||
compileFlags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
#endif
|
||||
|
||||
std::filesystem::path shaders_file = (std::filesystem::path(SHADERS_PATH) / std::filesystem::path("shaders.hlsl")).make_preferred();
|
||||
|
||||
if (FAILED(D3DCompileFromFile(shaders_file.wstring().c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr)))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Compile VSMain from shaders.hlsl", "Shader Compilation Failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (FAILED(D3DCompileFromFile(shaders_file.wstring().c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr)))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Compile PSMain from shaders.hlsl", "Shader Compilation Failure", MB_ICONERROR | MB_OK);
|
||||
return 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 = renderer->m_rootSignature.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;
|
||||
|
||||
if (FAILED(renderer->m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&renderer->m_pipelineState))))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Create Graphics Pipeline State Object", "PSO failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the command list.
|
||||
if (FAILED(renderer->m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, renderer->m_commandAllocator.Get(), renderer->m_pipelineState.Get(), IID_PPV_ARGS(&renderer->m_commandList))))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Create Command List", "Device failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (FAILED(renderer->m_commandList->Close()))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to close Command List", "Command List Failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the vertex buffer.
|
||||
{
|
||||
// Define the geometry for a triangle.
|
||||
RendererDX12::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 cHeapProps(D3D12_HEAP_TYPE_UPLOAD);
|
||||
auto bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
|
||||
HRESULT hr = renderer->m_device->CreateCommittedResource(
|
||||
&cHeapProps,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&bufferDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&renderer->m_vertexBuffer));
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Create Vertex Buffer", "Device Failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (FAILED(renderer->m_vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin))))
|
||||
{
|
||||
MessageBox(nullptr, "Failed to Map Vertex Buffer", "Vertex Buffer Failure", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
|
||||
renderer->m_vertexBuffer->Unmap(0, nullptr);
|
||||
|
||||
// Initialize the vertex buffer view.
|
||||
renderer->m_vertexBufferView.BufferLocation = renderer->m_vertexBuffer->GetGPUVirtualAddress();
|
||||
renderer->m_vertexBufferView.StrideInBytes = sizeof(RendererDX12::Vertex);
|
||||
renderer->m_vertexBufferView.SizeInBytes = vertexBufferSize;
|
||||
}
|
||||
|
||||
|
||||
if (FAILED(renderer->m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&renderer->m_fence))))
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create Fence", "Device Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->m_fenceValue = 1;
|
||||
|
||||
renderer->m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
if (renderer->m_fenceEvent == nullptr)
|
||||
{
|
||||
MessageBoxA(hWnd, "Failed to create Fence Event Handle", "Win32API Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->WaitForPreviousFrame();
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
} // namespace dx12_starter
|
||||
158
src/dx12_starter_dxgi.cpp
Normal file
158
src/dx12_starter_dxgi.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "pch.h"
|
||||
#include "dx12_starter_dxgi.h"
|
||||
|
||||
namespace dxgi
|
||||
{
|
||||
using PFNDXGIGETDEBUGINTERFACE = HRESULT(*)(const IID&, void**);
|
||||
static PFNDXGIGETDEBUGINTERFACE DXGIGetDebugInterfaceFunc;
|
||||
|
||||
void EnableDebugLayer()
|
||||
{
|
||||
#if _DEBUG
|
||||
if (ComPtr<ID3D12Debug6> debug6; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug6.GetAddressOf()))))
|
||||
{
|
||||
debug6->EnableDebugLayer();
|
||||
}
|
||||
else if (ComPtr<ID3D12Debug5> debug5; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug5.GetAddressOf()))))
|
||||
{
|
||||
debug5->EnableDebugLayer();
|
||||
}
|
||||
else if (ComPtr<ID3D12Debug4> debug4; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug4.GetAddressOf()))))
|
||||
{
|
||||
debug4->EnableDebugLayer();
|
||||
}
|
||||
else if (ComPtr<ID3D12Debug3> debug3; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug3.GetAddressOf()))))
|
||||
{
|
||||
debug3->EnableDebugLayer();
|
||||
}
|
||||
//Must be a bug in the API there is no EnableDebugLayer call
|
||||
//else if (ComPtr<ID3D12Debug2> debug2; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug2.GetAddressOf()))))
|
||||
//{
|
||||
// debug2->EnableDebugLayer();
|
||||
//}
|
||||
else if (ComPtr<ID3D12Debug1> debug1; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug1.GetAddressOf()))))
|
||||
{
|
||||
debug1->EnableDebugLayer();
|
||||
}
|
||||
else if (ComPtr<ID3D12Debug> debug; SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debug.GetAddressOf()))))
|
||||
{
|
||||
debug->EnableDebugLayer();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxA(nullptr, "Error: Cannot Enable Debug Layer", "Debug Layer Failure", MB_ICONWARNING | MB_OK);
|
||||
}
|
||||
|
||||
const auto onInfoQueueCreated = [](IDXGIInfoQueue* dxgiInfoQueue) {
|
||||
dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true);
|
||||
dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true);
|
||||
|
||||
DXGI_INFO_QUEUE_MESSAGE_ID hide[] =
|
||||
{
|
||||
80 /* IDXGISwapChain::GetContainingOutput: The swapchain's adapter does not control the output on which the swapchain's window resides. */,
|
||||
};
|
||||
|
||||
DXGI_INFO_QUEUE_FILTER filter = {};
|
||||
filter.DenyList.NumIDs = _countof(hide);
|
||||
filter.DenyList.pIDList = hide;
|
||||
dxgiInfoQueue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter);
|
||||
};
|
||||
|
||||
ComPtr<IDXGIInfoQueue> infoQueue{};
|
||||
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(infoQueue.GetAddressOf()))))
|
||||
{
|
||||
onInfoQueueCreated(infoQueue.Get());
|
||||
}
|
||||
else if (DXGIGetDebugInterfaceFunc && SUCCEEDED(DXGIGetDebugInterfaceFunc(IID_PPV_ARGS(infoQueue.GetAddressOf()))))
|
||||
{
|
||||
onInfoQueueCreated(infoQueue.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxA(nullptr, "Error: Cannot Create Debug Interface", "Debug Interface Failure", MB_ICONWARNING | MB_OK);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto CreateAdapter(ComPtr<IDXGIFactory> factory) -> ComPtr<IDXGIAdapter1>
|
||||
{
|
||||
ComPtr<IDXGIAdapter1> adapter{};
|
||||
if (ComPtr<IDXGIFactory6> factory6{}; SUCCEEDED(factory.As<IDXGIFactory6>(&factory6)))
|
||||
{
|
||||
for (UINT adapterIndex = 0;
|
||||
SUCCEEDED(factory6->EnumAdapterByGpuPreference(
|
||||
adapterIndex,
|
||||
DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
|
||||
IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())));
|
||||
adapterIndex++)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
if (FAILED(adapter->GetDesc1(&desc)))
|
||||
{
|
||||
MessageBoxA(nullptr, "Error: Failed to get the adapter description", "Adapter Desc Error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
|
||||
{
|
||||
// Don't select the Basic Render Driver adapter.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto DebugPrintInfo = [](UINT adapterIndex, const DXGI_ADAPTER_DESC1& desc) {
|
||||
#ifdef _DEBUG
|
||||
wchar_t buff[256] = {};
|
||||
swprintf_s(buff, L"Direct3D Adapter (%u): VID:%04X, PID:%04X - %ls\n", adapterIndex, desc.VendorId, desc.DeviceId, desc.Description);
|
||||
OutputDebugStringW(buff);
|
||||
#endif
|
||||
};
|
||||
|
||||
// Check to see if the adapter supports Direct3D 12, but don't create the actual device yet.
|
||||
if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
|
||||
{
|
||||
DebugPrintInfo(adapterIndex, desc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
auto CreateSwapChain(ComPtr<IDXGIFactory> factory, ComPtr<ID3D12CommandQueue> commandQueue, HWND hWnd, UINT width, UINT height) -> ComPtr<IDXGISwapChain>
|
||||
{
|
||||
if (ComPtr<IDXGIFactory4> factory4{}; SUCCEEDED(factory.As<IDXGIFactory4>(&factory4)))
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC1 desc = {
|
||||
.Width = width,
|
||||
.Height = height,
|
||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
.Stereo = FALSE,
|
||||
.SampleDesc = {
|
||||
.Count = 1,
|
||||
.Quality = 0},
|
||||
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
.BufferCount = 2,
|
||||
.Scaling = DXGI_SCALING_STRETCH,
|
||||
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
|
||||
.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED,
|
||||
.Flags = 0};
|
||||
|
||||
ComPtr<IDXGISwapChain1> swapChain{};
|
||||
if (FAILED(factory4->CreateSwapChainForHwnd(commandQueue.Get(), hWnd, &desc, nullptr, nullptr, &swapChain)))
|
||||
{
|
||||
MessageBox(hWnd, "Failed to create swap chain", "Swap chain error", MB_ICONERROR | MB_OK);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (FAILED(factory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER)))
|
||||
{
|
||||
MessageBox(hWnd, "Failed to disable alt enter", "Swap chain error", MB_ICONWARNING | MB_OK);
|
||||
}
|
||||
|
||||
return swapChain;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
6
src/dx12_starter_dxgi.h
Normal file
6
src/dx12_starter_dxgi.h
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace dxgi
|
||||
{
|
||||
void EnableDebugLayer();
|
||||
auto CreateAdapter(ComPtr<IDXGIFactory> factory) -> ComPtr<IDXGIAdapter1>;
|
||||
auto CreateSwapChain(ComPtr<IDXGIFactory> factory, ComPtr<ID3D12CommandQueue> commandQueue, HWND hWnd, UINT width, UINT height) -> ComPtr<IDXGISwapChain>;
|
||||
} // namespace dxgi
|
||||
5
src/dxgi.h
Normal file
5
src/dxgi.h
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace dxgi
|
||||
{
|
||||
void EnableDebugLayer();
|
||||
void CreateFactory();
|
||||
}
|
||||
24
src/main.cpp
24
src/main.cpp
@@ -1,9 +1,13 @@
|
||||
#include "pch.h"
|
||||
#include "window.h"
|
||||
#include "device.h"
|
||||
#include "renderer.h"
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
Microsoft::WRL::Wrappers::RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
|
||||
if (FAILED(initialize))
|
||||
return 1;
|
||||
|
||||
auto window = dx12_starter::IWindow::Create({
|
||||
.x = CW_USEDEFAULT,
|
||||
.y = CW_USEDEFAULT,
|
||||
@@ -12,19 +16,25 @@ auto main() -> int
|
||||
.name = "Starter Dx12"
|
||||
});
|
||||
|
||||
auto renderer = dx12_starter::IDevice::Create({
|
||||
auto renderer = dx12_starter::IRenderer::Create({
|
||||
.nativeWindowHandle = window->GetNativeHandle(),
|
||||
.versionMajor = 4,
|
||||
.versionMinor = 6
|
||||
.minimumVersion = {
|
||||
.major = 11,
|
||||
.minor = 0
|
||||
},
|
||||
.frameCount = 2
|
||||
});
|
||||
|
||||
if (renderer == nullptr)
|
||||
return 1;
|
||||
|
||||
while (window->IsOpen())
|
||||
{
|
||||
window->PumpMessages();
|
||||
|
||||
renderer->ClearBuffers();
|
||||
renderer->DrawScene();
|
||||
renderer->Present();
|
||||
renderer->RecordCommands();
|
||||
renderer->Render();
|
||||
renderer->WaitForPreviousFrame();
|
||||
}
|
||||
|
||||
renderer->Destroy();
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
52
src/pch.h
52
src/pch.h
@@ -1,14 +1,56 @@
|
||||
#ifndef DX12_STARTER_PCH_H_INCLUDED
|
||||
#define DX12_STARTER_PCH_H_INCLUDED
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif //WIN32_LEAN_AND_MEAN
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4265)
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#include <winsdkver.h>
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
#include <sdkddkver.h>
|
||||
|
||||
#define NODRAWTEXT
|
||||
#define NOGDI
|
||||
#define NOBITMAP
|
||||
#define NOMXC
|
||||
#define NOSERVICE
|
||||
#define NOHELP
|
||||
#define NOMINMAX
|
||||
#endif //NOMINMAX
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <wrl/client.h>
|
||||
#include <wrl/event.h>
|
||||
|
||||
#include <roapi.h>
|
||||
|
||||
template<typename T>
|
||||
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||
|
||||
#if defined(NTDDI_WIN10_RS2)
|
||||
#include <dxgi1_6.h>
|
||||
#else
|
||||
#include <dxgi1_5.h>
|
||||
#endif
|
||||
|
||||
#include <d3d12sdklayers.h>
|
||||
#include <d3dcompiler.h>
|
||||
|
||||
#include "d3dx12.h"
|
||||
|
||||
#if _DEBUG
|
||||
#include <dxgidebug.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <span>
|
||||
#include <numeric>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cinttypes>
|
||||
#include <filesystem>
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif //DX12_STARTER_PCH_H_INCLUDED
|
||||
31
src/shaders.hlsl
Normal file
31
src/shaders.hlsl
Normal file
@@ -0,0 +1,31 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
struct PSInput
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
};
|
||||
|
||||
PSInput VSMain(float4 position : POSITION, float4 color : COLOR)
|
||||
{
|
||||
PSInput result;
|
||||
|
||||
result.position = position;
|
||||
result.color = color;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float4 PSMain(PSInput input) : SV_TARGET
|
||||
{
|
||||
return input.color;
|
||||
}
|
||||
Reference in New Issue
Block a user