mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-16 00:17:03 +00:00
Swap chains, part 2 (#94)
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
#ifndef UTILS_BACKENDBINDING_H_
|
||||
#define UTILS_BACKENDBINDING_H_
|
||||
|
||||
#include <nxt/nxt_wsi.h>
|
||||
|
||||
struct GLFWwindow;
|
||||
typedef struct nxtProcTable_s nxtProcTable;
|
||||
typedef struct nxtDeviceImpl* nxtDevice;
|
||||
@@ -35,7 +37,7 @@ namespace utils {
|
||||
|
||||
virtual void SetupGLFWWindowHints() = 0;
|
||||
virtual void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) = 0;
|
||||
virtual void SwapBuffers() = 0;
|
||||
virtual uint64_t GetSwapChainImplementation() = 0;
|
||||
|
||||
void SetWindow(GLFWwindow* window);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ list(APPEND UTILS_SOURCES
|
||||
${UTILS_DIR}/BackendBinding.h
|
||||
${UTILS_DIR}/NXTHelpers.cpp
|
||||
${UTILS_DIR}/NXTHelpers.h
|
||||
${UTILS_DIR}/SwapChainImpl.h
|
||||
${UTILS_DIR}/SystemUtils.cpp
|
||||
${UTILS_DIR}/SystemUtils.h
|
||||
)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "utils/BackendBinding.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "nxt/nxt_wsi.h"
|
||||
#include "utils/SwapChainImpl.h"
|
||||
|
||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||
#include "GLFW/glfw3.h"
|
||||
@@ -34,7 +36,6 @@ namespace backend {
|
||||
namespace d3d12 {
|
||||
void Init(ComPtr<ID3D12Device> d3d12Device, nxtProcTable* procs, nxtDevice* device);
|
||||
ComPtr<ID3D12CommandQueue> GetCommandQueue(nxtDevice device);
|
||||
void SetNextTexture(nxtDevice device, ComPtr<ID3D12Resource> resource);
|
||||
uint64_t GetSerial(const nxtDevice device);
|
||||
void NextSerial(nxtDevice device);
|
||||
void ExecuteCommandLists(nxtDevice device, std::initializer_list<ID3D12CommandList*> commandLists);
|
||||
@@ -44,48 +45,88 @@ namespace d3d12 {
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
namespace {
|
||||
void ASSERT_SUCCESS(HRESULT hr) {
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
class D3D12Binding : public BackendBinding {
|
||||
public:
|
||||
void SetupGLFWWindowHints() override {
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
}
|
||||
ComPtr<IDXGIFactory4> CreateFactory() {
|
||||
ComPtr<IDXGIFactory4> factory;
|
||||
|
||||
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
|
||||
uint32_t dxgiFactoryFlags = 0;
|
||||
uint32_t dxgiFactoryFlags = 0;
|
||||
#ifdef _DEBUG
|
||||
// Enable the debug layer (requires the Graphics Tools "optional feature").
|
||||
// NOTE: Enabling the debug layer after device creation will invalidate the active device.
|
||||
{
|
||||
ComPtr<ID3D12Debug> debugController;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
|
||||
debugController->EnableDebugLayer();
|
||||
// Enable the debug layer (requires the Graphics Tools "optional feature").
|
||||
// NOTE: Enabling the debug layer after device creation will invalidate the active device.
|
||||
{
|
||||
ComPtr<ID3D12Debug> debugController;
|
||||
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
|
||||
debugController->EnableDebugLayer();
|
||||
|
||||
// Enable additional debug layers.
|
||||
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
|
||||
}
|
||||
|
||||
ComPtr<IDXGIDebug1> dxgiDebug;
|
||||
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) {
|
||||
dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_ALL));
|
||||
}
|
||||
// Enable additional debug layers.
|
||||
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
|
||||
}
|
||||
|
||||
ComPtr<IDXGIDebug1> dxgiDebug;
|
||||
if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) {
|
||||
dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_ALL));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ASSERT_SUCCESS(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
|
||||
ASSERT(GetHardwareAdapter(factory.Get(), &hardwareAdapter));
|
||||
ASSERT_SUCCESS(D3D12CreateDevice(
|
||||
hardwareAdapter.Get(),
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
IID_PPV_ARGS(&d3d12Device)
|
||||
));
|
||||
ASSERT_SUCCESS(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
|
||||
|
||||
backend::d3d12::Init(d3d12Device, procs, device);
|
||||
backendDevice = *device;
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
class SwapChainImplD3D12 : SwapChainImpl {
|
||||
public:
|
||||
static nxtSwapChainImplementation Create(HWND window, const nxtProcTable& procs) {
|
||||
auto impl = GenerateSwapChainImplementation<SwapChainImplD3D12, nxtWSIContextD3D12>();
|
||||
impl.userData = new SwapChainImplD3D12(window, procs);
|
||||
return impl;
|
||||
}
|
||||
|
||||
private:
|
||||
nxtDevice backendDevice = nullptr;
|
||||
nxtProcTable procs = {};
|
||||
|
||||
static constexpr unsigned int kFrameCount = 2;
|
||||
|
||||
HWND window = 0;
|
||||
ComPtr<IDXGIFactory4> factory = {};
|
||||
ComPtr<ID3D12CommandQueue> commandQueue = {};
|
||||
ComPtr<IDXGISwapChain3> swapChain = {};
|
||||
ComPtr<ID3D12Resource> renderTargetResources[kFrameCount] = {};
|
||||
|
||||
// Frame synchronization. Updated every frame
|
||||
uint32_t renderTargetIndex = 0;
|
||||
uint32_t previousRenderTargetIndex = 0;
|
||||
uint64_t lastSerialRenderTargetWasUsed[kFrameCount] = {};
|
||||
|
||||
SwapChainImplD3D12(HWND window, nxtProcTable procs)
|
||||
: window(window), procs(procs), factory(CreateFactory()) {
|
||||
}
|
||||
|
||||
~SwapChainImplD3D12() {
|
||||
}
|
||||
|
||||
// For GenerateSwapChainImplementation
|
||||
friend class SwapChainImpl;
|
||||
|
||||
void Init(nxtWSIContextD3D12* ctx) {
|
||||
backendDevice = ctx->device;
|
||||
commandQueue = backend::d3d12::GetCommandQueue(backendDevice);
|
||||
}
|
||||
|
||||
nxtSwapChainError Configure(nxtTextureFormat format,
|
||||
uint32_t width, uint32_t height) {
|
||||
if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) {
|
||||
return "unsupported format";
|
||||
}
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
int width, height;
|
||||
glfwGetWindowSize(window, &width, &height);
|
||||
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
|
||||
swapChainDesc.Width = width;
|
||||
swapChainDesc.Height = height;
|
||||
@@ -96,11 +137,10 @@ namespace utils {
|
||||
swapChainDesc.SampleDesc.Count = 1;
|
||||
swapChainDesc.SampleDesc.Quality = 0;
|
||||
|
||||
HWND win32Window = glfwGetWin32Window(window);
|
||||
ComPtr<IDXGISwapChain1> swapChain1;
|
||||
ASSERT_SUCCESS(factory->CreateSwapChainForHwnd(
|
||||
commandQueue.Get(),
|
||||
win32Window,
|
||||
window,
|
||||
&swapChainDesc,
|
||||
nullptr,
|
||||
nullptr,
|
||||
@@ -122,48 +162,26 @@ namespace utils {
|
||||
lastSerialRenderTargetWasUsed[n] = initialSerial;
|
||||
}
|
||||
|
||||
// Transition the first frame to be a render target
|
||||
{
|
||||
backend::d3d12::OpenCommandList(backendDevice, &commandList);
|
||||
|
||||
D3D12_RESOURCE_BARRIER resourceBarrier;
|
||||
resourceBarrier.Transition.pResource = renderTargetResources[renderTargetIndex].Get();
|
||||
resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
commandList->ResourceBarrier(1, &resourceBarrier);
|
||||
ASSERT_SUCCESS(commandList->Close());
|
||||
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
|
||||
|
||||
backend::d3d12::NextSerial(backendDevice);
|
||||
}
|
||||
|
||||
backend::d3d12::SetNextTexture(backendDevice, renderTargetResources[renderTargetIndex]);
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
|
||||
void SwapBuffers() override {
|
||||
// Transition current frame's render target for presenting
|
||||
{
|
||||
backend::d3d12::OpenCommandList(backendDevice, &commandList);
|
||||
D3D12_RESOURCE_BARRIER resourceBarrier;
|
||||
resourceBarrier.Transition.pResource = renderTargetResources[renderTargetIndex].Get();
|
||||
resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
commandList->ResourceBarrier(1, &resourceBarrier);
|
||||
ASSERT_SUCCESS(commandList->Close());
|
||||
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
|
||||
}
|
||||
nxtSwapChainError GetNextTexture(nxtSwapChainNextTexture* nextTexture) {
|
||||
nextTexture->texture = renderTargetResources[renderTargetIndex].Get();
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
|
||||
nxtSwapChainError Present() {
|
||||
// Current frame already transitioned to Present by the application, but
|
||||
// we need to flush the D3D12 backend's pending transitions.
|
||||
procs.deviceTick(backendDevice);
|
||||
|
||||
ASSERT_SUCCESS(swapChain->Present(1, 0));
|
||||
|
||||
// Transition last frame's render target back to being a render target
|
||||
{
|
||||
ComPtr<ID3D12GraphicsCommandList> commandList = {};
|
||||
backend::d3d12::OpenCommandList(backendDevice, &commandList);
|
||||
|
||||
D3D12_RESOURCE_BARRIER resourceBarrier;
|
||||
resourceBarrier.Transition.pResource = renderTargetResources[previousRenderTargetIndex].Get();
|
||||
resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
@@ -188,32 +206,47 @@ namespace utils {
|
||||
|
||||
lastSerialRenderTargetWasUsed[renderTargetIndex] = backend::d3d12::GetSerial(backendDevice);
|
||||
|
||||
// Tell the backend to render to the current render target
|
||||
backend::d3d12::SetNextTexture(backendDevice, renderTargetResources[renderTargetIndex]);
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
class D3D12Binding : public BackendBinding {
|
||||
public:
|
||||
void SetupGLFWWindowHints() override {
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
}
|
||||
|
||||
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
|
||||
factory = CreateFactory();
|
||||
ASSERT(GetHardwareAdapter(factory.Get(), &hardwareAdapter));
|
||||
ASSERT_SUCCESS(D3D12CreateDevice(
|
||||
hardwareAdapter.Get(),
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
IID_PPV_ARGS(&d3d12Device)
|
||||
));
|
||||
|
||||
backend::d3d12::Init(d3d12Device, procs, device);
|
||||
backendDevice = *device;
|
||||
procTable = *procs;
|
||||
}
|
||||
|
||||
uint64_t GetSwapChainImplementation() override {
|
||||
if (swapchainImpl.userData == nullptr) {
|
||||
HWND win32Window = glfwGetWin32Window(window);
|
||||
swapchainImpl = SwapChainImplD3D12::Create(win32Window, procTable);
|
||||
}
|
||||
return reinterpret_cast<uint64_t>(&swapchainImpl);
|
||||
}
|
||||
|
||||
private:
|
||||
nxtDevice backendDevice = nullptr;
|
||||
|
||||
static constexpr unsigned int kFrameCount = 2;
|
||||
nxtSwapChainImplementation swapchainImpl = {};
|
||||
nxtProcTable procTable = {};
|
||||
|
||||
// Initialization
|
||||
ComPtr<IDXGIFactory4> factory;
|
||||
ComPtr<IDXGIAdapter1> hardwareAdapter;
|
||||
ComPtr<ID3D12Device> d3d12Device;
|
||||
ComPtr<ID3D12CommandQueue> commandQueue;
|
||||
ComPtr<IDXGISwapChain3> swapChain;
|
||||
ComPtr<ID3D12Resource> renderTargetResources[kFrameCount];
|
||||
|
||||
// Frame synchronization. Updated every frame
|
||||
uint32_t renderTargetIndex;
|
||||
uint32_t previousRenderTargetIndex;
|
||||
uint64_t lastSerialRenderTargetWasUsed[kFrameCount];
|
||||
ComPtr<ID3D12GraphicsCommandList> commandList;
|
||||
|
||||
static void ASSERT_SUCCESS(HRESULT hr) {
|
||||
ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
static bool GetHardwareAdapter(IDXGIFactory4* factory, IDXGIAdapter1** hardwareAdapter) {
|
||||
*hardwareAdapter = nullptr;
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
#include "utils/BackendBinding.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "nxt/nxt_wsi.h"
|
||||
#include "utils/SwapChainImpl.h"
|
||||
|
||||
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "GLFW/glfw3native.h"
|
||||
@@ -30,6 +34,103 @@ namespace metal {
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
class SwapChainImplMTL : SwapChainImpl {
|
||||
public:
|
||||
static nxtSwapChainImplementation Create(id nswindow) {
|
||||
auto impl = GenerateSwapChainImplementation<SwapChainImplMTL, nxtWSIContextMetal>();
|
||||
impl.userData = new SwapChainImplMTL(nswindow);
|
||||
return impl;
|
||||
}
|
||||
|
||||
private:
|
||||
id nsWindow = nil;
|
||||
id<MTLDevice> mtlDevice = nil;
|
||||
id<MTLCommandQueue> commandQueue = nil;
|
||||
|
||||
CAMetalLayer* layer = nullptr;
|
||||
id<CAMetalDrawable> currentDrawable = nil;
|
||||
id<MTLTexture> currentTexture = nil;
|
||||
|
||||
SwapChainImplMTL(id nsWindow)
|
||||
: nsWindow(nsWindow) {
|
||||
}
|
||||
|
||||
~SwapChainImplMTL() {
|
||||
[currentTexture release];
|
||||
[currentDrawable release];
|
||||
}
|
||||
|
||||
// For GenerateSwapChainImplementation
|
||||
friend class SwapChainImpl;
|
||||
|
||||
void Init(nxtWSIContextMetal* ctx) {
|
||||
mtlDevice = ctx->device;
|
||||
commandQueue = [mtlDevice newCommandQueue];
|
||||
}
|
||||
|
||||
nxtSwapChainError Configure(nxtTextureFormat format,
|
||||
uint32_t width, uint32_t height) {
|
||||
if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) {
|
||||
return "unsupported format";
|
||||
}
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
|
||||
NSView* contentView = [nsWindow contentView];
|
||||
[contentView setWantsLayer: YES];
|
||||
|
||||
CGSize size = {};
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
|
||||
layer = [CAMetalLayer layer];
|
||||
[layer setDevice: mtlDevice];
|
||||
[layer setPixelFormat: MTLPixelFormatBGRA8Unorm];
|
||||
[layer setFramebufferOnly: YES];
|
||||
[layer setDrawableSize: size];
|
||||
|
||||
[contentView setLayer: layer];
|
||||
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
|
||||
nxtSwapChainError GetNextTexture(nxtSwapChainNextTexture* nextTexture) {
|
||||
[currentDrawable release];
|
||||
currentDrawable = [layer nextDrawable];
|
||||
[currentDrawable retain];
|
||||
|
||||
[currentTexture release];
|
||||
currentTexture = currentDrawable.texture;
|
||||
[currentTexture retain];
|
||||
|
||||
// Clear initial contents of the texture
|
||||
{
|
||||
MTLRenderPassDescriptor* passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
passDescriptor.colorAttachments[0].texture = currentTexture;
|
||||
passDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||
passDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
|
||||
id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer
|
||||
renderCommandEncoderWithDescriptor:passDescriptor];
|
||||
[commandEncoder endEncoding];
|
||||
[commandBuffer commit];
|
||||
}
|
||||
|
||||
nextTexture->texture = reinterpret_cast<void*>(currentTexture);
|
||||
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
|
||||
nxtSwapChainError Present() {
|
||||
id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
|
||||
[commandBuffer presentDrawable: currentDrawable];
|
||||
[commandBuffer commit];
|
||||
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
class MetalBinding : public BackendBinding {
|
||||
public:
|
||||
@@ -39,40 +140,21 @@ namespace utils {
|
||||
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
|
||||
metalDevice = MTLCreateSystemDefaultDevice();
|
||||
|
||||
id nsWindow = glfwGetCocoaWindow(window);
|
||||
NSView* contentView = [nsWindow contentView];
|
||||
[contentView setWantsLayer: YES];
|
||||
|
||||
layer = [CAMetalLayer layer];
|
||||
[layer setDevice: metalDevice];
|
||||
[layer setPixelFormat: MTLPixelFormatBGRA8Unorm];
|
||||
[layer setFramebufferOnly: YES];
|
||||
[layer setDrawableSize: [contentView bounds].size];
|
||||
|
||||
[contentView setLayer: layer];
|
||||
|
||||
backend::metal::Init(metalDevice, procs, device);
|
||||
backendDevice = *device;
|
||||
|
||||
backend::metal::SetNextDrawable(backendDevice, GetNextDrawable());
|
||||
}
|
||||
void SwapBuffers() override {
|
||||
backend::metal::Present(backendDevice);
|
||||
backend::metal::SetNextDrawable(backendDevice, GetNextDrawable());
|
||||
|
||||
uint64_t GetSwapChainImplementation() override {
|
||||
if (swapchainImpl.userData == nullptr) {
|
||||
swapchainImpl = SwapChainImplMTL::Create(glfwGetCocoaWindow(window));
|
||||
}
|
||||
return reinterpret_cast<uint64_t>(&swapchainImpl);
|
||||
}
|
||||
|
||||
private:
|
||||
id<CAMetalDrawable> GetNextDrawable() {
|
||||
lastDrawable = [layer nextDrawable];
|
||||
return lastDrawable;
|
||||
}
|
||||
|
||||
id<MTLDevice> metalDevice = nil;
|
||||
CAMetalLayer* layer = nullptr;
|
||||
|
||||
id<CAMetalDrawable> lastDrawable = nil;
|
||||
|
||||
nxtDevice backendDevice = nullptr;
|
||||
nxtSwapChainImplementation swapchainImpl = {};
|
||||
};
|
||||
|
||||
BackendBinding* CreateMetalBinding() {
|
||||
|
||||
@@ -92,34 +92,6 @@ namespace utils {
|
||||
return builder.GetResult();
|
||||
}
|
||||
|
||||
void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer) {
|
||||
auto depthStencilTexture = device.CreateTextureBuilder()
|
||||
.SetDimension(nxt::TextureDimension::e2D)
|
||||
.SetExtent(640, 480, 1)
|
||||
.SetFormat(nxt::TextureFormat::D32FloatS8Uint)
|
||||
.SetMipLevels(1)
|
||||
.SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment)
|
||||
.GetResult();
|
||||
depthStencilTexture.FreezeUsage(nxt::TextureUsageBit::OutputAttachment);
|
||||
auto depthStencilView = depthStencilTexture.CreateTextureViewBuilder()
|
||||
.GetResult();
|
||||
|
||||
*renderPass = device.CreateRenderPassBuilder()
|
||||
.SetAttachmentCount(2)
|
||||
.AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm)
|
||||
.AttachmentSetFormat(1, nxt::TextureFormat::D32FloatS8Uint)
|
||||
.SetSubpassCount(1)
|
||||
.SubpassSetColorAttachment(0, 0, 0)
|
||||
.SubpassSetDepthStencilAttachment(0, 1)
|
||||
.GetResult();
|
||||
*framebuffer = device.CreateFramebufferBuilder()
|
||||
.SetRenderPass(*renderPass)
|
||||
.SetDimensions(640, 480)
|
||||
// Attachment 0 is implicit until we add WSI
|
||||
.SetAttachment(1, depthStencilView)
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
nxt::Buffer CreateFrozenBufferFromData(const nxt::Device& device, const void* data, uint32_t size, nxt::BufferUsageBit usage) {
|
||||
nxt::Buffer buffer = device.CreateBufferBuilder()
|
||||
.SetAllowedUsage(nxt::BufferUsageBit::TransferDst | usage)
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace utils {
|
||||
|
||||
void FillShaderModuleBuilder(const nxt::ShaderModuleBuilder& builder, nxt::ShaderStage stage, const char* source);
|
||||
nxt::ShaderModule CreateShaderModule(const nxt::Device& device, nxt::ShaderStage stage, const char* source);
|
||||
void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer);
|
||||
nxt::Buffer CreateFrozenBufferFromData(const nxt::Device& device, const void* data, uint32_t size, nxt::BufferUsageBit usage);
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace utils {
|
||||
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
|
||||
backend::null::Init(procs, device);
|
||||
}
|
||||
void SwapBuffers() override {
|
||||
uint64_t GetSwapChainImplementation() override {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,20 +14,104 @@
|
||||
|
||||
#include "utils/BackendBinding.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "common/Platform.h"
|
||||
#include "nxt/nxt_wsi.h"
|
||||
#include "utils/SwapChainImpl.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include "glad/glad.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
namespace backend {
|
||||
namespace opengl {
|
||||
void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device);
|
||||
void HACKCLEAR(nxtDevice device);
|
||||
void InitBackbuffer(nxtDevice device);
|
||||
void CommitBackbuffer(nxtDevice device);
|
||||
}
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
class SwapChainImplGL : SwapChainImpl {
|
||||
public:
|
||||
static nxtSwapChainImplementation Create(GLFWwindow* window) {
|
||||
auto impl = GenerateSwapChainImplementation<SwapChainImplGL, nxtWSIContextGL>();
|
||||
impl.userData = new SwapChainImplGL(window);
|
||||
return impl;
|
||||
}
|
||||
|
||||
private:
|
||||
GLFWwindow* window = nullptr;
|
||||
uint32_t cfgWidth = 0;
|
||||
uint32_t cfgHeight = 0;
|
||||
GLuint backFBO = 0;
|
||||
GLuint backTexture = 0;
|
||||
|
||||
SwapChainImplGL(GLFWwindow* window)
|
||||
: window(window) {
|
||||
}
|
||||
|
||||
~SwapChainImplGL() {
|
||||
glDeleteTextures(1, &backTexture);
|
||||
glDeleteFramebuffers(1, &backFBO);
|
||||
}
|
||||
|
||||
void HACKCLEAR() {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backFBO);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
// For GenerateSwapChainImplementation
|
||||
friend class SwapChainImpl;
|
||||
|
||||
void Init(nxtWSIContextGL*) {
|
||||
glGenTextures(1, &backTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, backTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
glGenFramebuffers(1, &backFBO);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, backFBO);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, backTexture, 0);
|
||||
}
|
||||
|
||||
nxtSwapChainError Configure(nxtTextureFormat format,
|
||||
uint32_t width, uint32_t height) {
|
||||
if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) {
|
||||
return "unsupported format";
|
||||
}
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
cfgWidth = width;
|
||||
cfgHeight = height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, backTexture);
|
||||
// Reallocate the texture
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
// Clear the newly (re-)allocated texture
|
||||
HACKCLEAR();
|
||||
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
|
||||
nxtSwapChainError GetNextTexture(nxtSwapChainNextTexture* nextTexture) {
|
||||
nextTexture->texture = reinterpret_cast<void*>(static_cast<size_t>(backTexture));
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
|
||||
nxtSwapChainError Present() {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, backFBO);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBlitFramebuffer(0, 0, cfgWidth, cfgHeight, 0, 0, cfgWidth, cfgHeight,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glfwSwapBuffers(window);
|
||||
HACKCLEAR();
|
||||
|
||||
return NXT_SWAP_CHAIN_NO_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
class OpenGLBinding : public BackendBinding {
|
||||
public:
|
||||
void SetupGLFWWindowHints() override {
|
||||
@@ -48,16 +132,18 @@ namespace utils {
|
||||
backend::opengl::Init(reinterpret_cast<void*(*)(const char*)>(glfwGetProcAddress), procs, device);
|
||||
|
||||
backendDevice = *device;
|
||||
backend::opengl::InitBackbuffer(backendDevice);
|
||||
}
|
||||
void SwapBuffers() override {
|
||||
backend::opengl::CommitBackbuffer(backendDevice);
|
||||
glfwSwapBuffers(window);
|
||||
backend::opengl::HACKCLEAR(backendDevice);
|
||||
|
||||
uint64_t GetSwapChainImplementation() override {
|
||||
if (swapchainImpl.userData == nullptr) {
|
||||
swapchainImpl = SwapChainImplGL::Create(window);
|
||||
}
|
||||
return reinterpret_cast<uint64_t>(&swapchainImpl);
|
||||
}
|
||||
|
||||
private:
|
||||
nxtDevice backendDevice = nullptr;
|
||||
nxtSwapChainImplementation swapchainImpl = {};
|
||||
};
|
||||
|
||||
BackendBinding* CreateOpenGLBinding() {
|
||||
|
||||
47
src/utils/SwapChainImpl.h
Normal file
47
src/utils/SwapChainImpl.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2017 The NXT Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef UTILS_SWAPCHAINIMPL_H_
|
||||
#define UTILS_SWAPCHAINIMPL_H_
|
||||
|
||||
namespace utils {
|
||||
class SwapChainImpl {
|
||||
protected:
|
||||
template<class TImpl, typename TWSIContext>
|
||||
static nxtSwapChainImplementation GenerateSwapChainImplementation() {
|
||||
nxtSwapChainImplementation impl = {};
|
||||
impl.Init = [](void* userData, void* wsiContext) {
|
||||
auto* ctx = reinterpret_cast<TWSIContext*>(wsiContext);
|
||||
reinterpret_cast<TImpl*>(userData)->Init(ctx);
|
||||
};
|
||||
impl.Destroy = [](void* userData) {
|
||||
delete reinterpret_cast<TImpl*>(userData);
|
||||
};
|
||||
impl.Configure = [](void* userData, nxtTextureFormat format, uint32_t width, uint32_t height) {
|
||||
return reinterpret_cast<TImpl*>(userData)->Configure(
|
||||
format, width, height);
|
||||
};
|
||||
impl.GetNextTexture = [](void* userData, nxtSwapChainNextTexture* nextTexture) {
|
||||
return reinterpret_cast<TImpl*>(userData)->GetNextTexture(
|
||||
nextTexture);
|
||||
};
|
||||
impl.Present = [](void* userData) {
|
||||
return reinterpret_cast<TImpl*>(userData)->Present();
|
||||
};
|
||||
return impl;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // UTILS_SWAPCHAINIMPL_H_
|
||||
Reference in New Issue
Block a user