Split BackendBinding from example/Utils into src/utils

This will make it possible to reuse the backend bindings for test suites
This commit is contained in:
Corentin Wallez 2017-06-19 12:53:38 -04:00 committed by Corentin Wallez
parent 931e6e82fd
commit 1bd219d8a8
13 changed files with 596 additions and 471 deletions

View File

@ -79,6 +79,7 @@ set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_subdirectory(src/backend)
add_subdirectory(src/wire)
add_subdirectory(src/utils)
add_subdirectory(src/tests)
add_subdirectory(examples)

View File

@ -12,67 +12,52 @@
# See the License for the specific language governing permissions and
# limitations under the License.
list(APPEND UTILS_SOURCES
Utils.h
add_library(sample_utils
Utils.cpp
BackendBinding.h
Utils.h
)
if (APPLE)
list(APPEND UTILS_SOURCES
MetalBinding.mm
)
endif()
if (WIN32)
list(APPEND UTILS_SOURCES
D3D12Binding.cpp
)
endif()
add_library(utils STATIC ${UTILS_SOURCES})
target_link_libraries(utils nxt_backend nxt_wire shaderc nxtcpp nxt)
SetCXX14(utils)
target_link_libraries(sample_utils utils nxt_wire)
SetCXX14(sample_utils)
add_executable(CHelloTriangle CHelloTriangle.cpp)
target_link_libraries(CHelloTriangle utils)
target_link_libraries(CHelloTriangle sample_utils)
SetCXX14(CHelloTriangle)
add_executable(CppHelloTriangle HelloTriangle.cpp)
target_link_libraries(CppHelloTriangle utils)
target_link_libraries(CppHelloTriangle sample_utils)
SetCXX14(CppHelloTriangle)
add_executable(ComputeBoids ComputeBoids.cpp)
target_link_libraries(ComputeBoids utils)
target_link_libraries(ComputeBoids sample_utils)
target_include_directories(ComputeBoids PUBLIC ../ ${GLM_INCLUDE_DIR})
SetCXX14(ComputeBoids)
add_executable(HelloVertices HelloVertices.cpp)
target_link_libraries(HelloVertices utils)
target_link_libraries(HelloVertices sample_utils)
SetCXX14(HelloVertices)
add_executable(HelloInstancing HelloInstancing.cpp)
target_link_libraries(HelloInstancing utils)
target_link_libraries(HelloInstancing sample_utils)
SetCXX14(HelloInstancing)
add_executable(HelloIndices HelloIndices.cpp)
target_link_libraries(HelloIndices utils)
target_link_libraries(HelloIndices sample_utils)
SetCXX14(HelloIndices)
add_executable(HelloUBO HelloUBO.cpp)
target_link_libraries(HelloUBO utils)
target_link_libraries(HelloUBO sample_utils)
SetCXX14(HelloUBO)
add_executable(HelloCompute HelloCompute.cpp)
target_link_libraries(HelloCompute utils)
target_link_libraries(HelloCompute sample_utils)
SetCXX14(HelloCompute)
add_executable(RenderToTexture RenderToTexture.cpp)
target_link_libraries(RenderToTexture utils)
target_link_libraries(RenderToTexture sample_utils)
SetCXX14(RenderToTexture)
add_executable(Animometer Animometer.cpp)
target_link_libraries(Animometer utils)
target_link_libraries(Animometer sample_utils)
SetCXX14(Animometer)
add_executable(SpirvTest SpirvTest.cpp)
@ -80,7 +65,7 @@ target_link_libraries(SpirvTest shaderc spirv-cross nxtcpp)
SetCXX14(SpirvTest)
add_executable(CppHelloDepthStencil HelloDepthStencil.cpp)
target_link_libraries(CppHelloDepthStencil utils)
target_link_libraries(CppHelloDepthStencil sample_utils)
SetCXX14(CppHelloDepthStencil)
add_subdirectory(glTFViewer)

View File

@ -1,254 +0,0 @@
// 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.
#include "BackendBinding.h"
#define GLFW_EXPOSE_NATIVE_WIN32
#include "GLFW/glfw3.h"
#include "GLFW/glfw3native.h"
#include <initializer_list>
#include <assert.h>
#include <wrl.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#define ASSERT assert
using Microsoft::WRL::ComPtr;
namespace backend {
namespace d3d12 {
void Init(ComPtr<ID3D12Device> d3d12Device, nxtProcTable* procs, nxtDevice* device);
ComPtr<ID3D12CommandQueue> GetCommandQueue(nxtDevice device);
void SetNextRenderTargetDescriptor(nxtDevice device, D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor);
uint64_t GetSerial(const nxtDevice device);
void NextSerial(nxtDevice device);
void ExecuteCommandLists(nxtDevice device, std::initializer_list<ID3D12CommandList*> commandLists);
void WaitForSerial(nxtDevice device, uint64_t serial);
void OpenCommandList(nxtDevice device, ComPtr<ID3D12GraphicsCommandList>* commandList);
}
}
class D3D12Binding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
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 additional debug layers.
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
}
}
#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)
));
backend::d3d12::Init(d3d12Device, procs, device);
backendDevice = *device;
commandQueue = backend::d3d12::GetCommandQueue(backendDevice);
int width, height;
glfwGetWindowSize(window, &width, &height);
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = width;
swapChainDesc.Height = height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = kFrameCount;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
HWND win32Window = glfwGetWin32Window(window);
ComPtr<IDXGISwapChain1> swapChain1;
ASSERT_SUCCESS(factory->CreateSwapChainForHwnd(
commandQueue.Get(),
win32Window,
&swapChainDesc,
nullptr,
nullptr,
&swapChain1
));
ASSERT_SUCCESS(swapChain1.As(&swapChain));
// Describe and create a render target view (RTV) descriptor heap.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = kFrameCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ASSERT_SUCCESS(d3d12Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap)));
rtvDescriptorSize = d3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// Create a RTV for each frame.
{
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart();
for (uint32_t n = 0; n < kFrameCount; ++n) {
ASSERT_SUCCESS(swapChain->GetBuffer(n, IID_PPV_ARGS(&renderTargetResources[n])));
d3d12Device->CreateRenderTargetView(renderTargetResources[n].Get(), nullptr, renderTargetViewHandle);
renderTargetViewHandle.ptr += rtvDescriptorSize;
}
}
// Get the initial render target and arbitrarily choose a "previous" render target that's different
previousRenderTargetIndex = renderTargetIndex = swapChain->GetCurrentBackBufferIndex();
previousRenderTargetIndex = renderTargetIndex == 0 ? 1 : 0;
// Initial the serial for all render targets
const uint64_t initialSerial = backend::d3d12::GetSerial(backendDevice);
for (uint32_t n = 0; n < kFrameCount; ++n) {
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());
ID3D12CommandList* commandLists[] = { commandList.Get() };
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
backend::d3d12::NextSerial(backendDevice);
}
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart();
renderTargetViewHandle.ptr += rtvDescriptorSize * renderTargetIndex;
backend::d3d12::SetNextRenderTargetDescriptor(backendDevice, renderTargetViewHandle);
}
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());
ID3D12CommandList* commandLists[] = { commandList.Get() };
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
}
ASSERT_SUCCESS(swapChain->Present(1, 0));
// Transition last frame's render target back to being a render target
{
backend::d3d12::OpenCommandList(backendDevice, &commandList);
D3D12_RESOURCE_BARRIER resourceBarrier;
resourceBarrier.Transition.pResource = renderTargetResources[previousRenderTargetIndex].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());
ID3D12CommandList* commandLists[] = { commandList.Get() };
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
}
backend::d3d12::NextSerial(backendDevice);
previousRenderTargetIndex = renderTargetIndex;
renderTargetIndex = swapChain->GetCurrentBackBufferIndex();
// If the next render target is not ready to be rendered yet, wait until it is ready.
// If the last completed serial is less than the last requested serial for this render target,
// then the commands previously executed on this render target have not yet completed
backend::d3d12::WaitForSerial(backendDevice, lastSerialRenderTargetWasUsed[renderTargetIndex]);
lastSerialRenderTargetWasUsed[renderTargetIndex] = backend::d3d12::GetSerial(backendDevice);
// Tell the backend to render to the current render target
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart();
renderTargetViewHandle.ptr += rtvDescriptorSize * renderTargetIndex;
backend::d3d12::SetNextRenderTargetDescriptor(backendDevice, renderTargetViewHandle);
}
private:
nxtDevice backendDevice = nullptr;
static constexpr unsigned int kFrameCount = 2;
// Initialization
ComPtr<IDXGIFactory4> factory;
ComPtr<IDXGIAdapter1> hardwareAdapter;
ComPtr<ID3D12Device> d3d12Device;
ComPtr<ID3D12CommandQueue> commandQueue;
ComPtr<IDXGISwapChain3> swapChain;
ComPtr<ID3D12DescriptorHeap> renderTargetViewHeap;
ComPtr<ID3D12Resource> renderTargetResources[kFrameCount];
uint32_t rtvDescriptorSize;
// 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;
for (uint32_t adapterIndex = 0; ; ++adapterIndex) {
IDXGIAdapter1* adapter = nullptr;
if (factory->EnumAdapters1(adapterIndex, &adapter) == DXGI_ERROR_NOT_FOUND) {
break; // No more adapters to enumerate.
}
// Check to see if the adapter supports Direct3D 12, but don't create the actual device yet.
if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) {
*hardwareAdapter = adapter;
return true;
}
adapter->Release();
}
return false;
}
};
BackendBinding* CreateD3D12Binding() {
return new D3D12Binding;
}

View File

@ -1,78 +0,0 @@
// 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.
#include "BackendBinding.h"
#define GLFW_EXPOSE_NATIVE_COCOA
#include "GLFW/glfw3.h"
#include "GLFW/glfw3native.h"
#import <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.h>
namespace backend {
namespace metal {
void Init(id<MTLDevice> metalDevice, nxtProcTable* procs, nxtDevice* device);
void SetNextDrawable(nxtDevice device, id<CAMetalDrawable> drawable);
void Present(nxtDevice device);
}
}
class MetalBinding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}
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());
}
private:
id<CAMetalDrawable> GetNextDrawable() {
lastDrawable = [layer nextDrawable];
return lastDrawable;
}
id<MTLDevice> metalDevice = nil;
CAMetalLayer* layer = nullptr;
id<CAMetalDrawable> lastDrawable = nil;
nxtDevice backendDevice = nullptr;
};
BackendBinding* CreateMetalBinding() {
return new MetalBinding;
}

View File

@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "utils/BackendBinding.h"
#include "../src/wire/TerribleCommandBuffer.h"
#include <nxt/nxt.h>
#include <nxt/nxtcpp.h>
#include <shaderc/shaderc.hpp>
#include "GLFW/glfw3.h"
#include "BackendBinding.h"
#include "../src/wire/TerribleCommandBuffer.h"
#include <cstring>
#include <iostream>
#include <sstream>
@ -31,69 +31,10 @@
#include <unistd.h>
#endif
BackendBinding* CreateMetalBinding();
BackendBinding* CreateD3D12Binding();
namespace backend {
namespace opengl {
void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device);
void HACKCLEAR();
}
}
class OpenGLBinding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
#ifdef __APPLE__
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#endif
}
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
glfwMakeContextCurrent(window);
backend::opengl::Init(reinterpret_cast<void*(*)(const char*)>(glfwGetProcAddress), procs, device);
}
void SwapBuffers() override {
glfwSwapBuffers(window);
backend::opengl::HACKCLEAR();
}
};
namespace backend {
namespace null {
void Init(nxtProcTable* procs, nxtDevice* device);
}
}
class NullBinding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
}
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
backend::null::Init(procs, device);
}
void SwapBuffers() override {
}
};
void PrintDeviceError(const char* message, nxt::CallbackUserdata) {
std::cout << "Device error: " << message << std::endl;
}
enum class BackendType {
OpenGL,
Metal,
D3D12,
Null,
};
enum class CmdBufType {
None,
Terrible,
@ -101,15 +42,15 @@ enum class CmdBufType {
};
#if defined(__APPLE__)
static BackendType backendType = BackendType::Metal;
static utils::BackendType backendType = utils::BackendType::Metal;
#elif defined(_WIN32)
static BackendType backendType = BackendType::D3D12;
static utils::BackendType backendType = utils::BackendType::D3D12;
#else
static BackendType backendType = BackendType::OpenGL;
static utils::BackendType backendType = utils::BackendType::OpenGL;
#endif
static CmdBufType cmdBufType = CmdBufType::Terrible;
static BackendBinding* binding = nullptr;
static utils::BackendBinding* binding = nullptr;
static GLFWwindow* window = nullptr;
@ -119,27 +60,9 @@ static nxt::wire::TerribleCommandBuffer* c2sBuf = nullptr;
static nxt::wire::TerribleCommandBuffer* s2cBuf = nullptr;
nxt::Device CreateCppNXTDevice() {
switch (backendType) {
case BackendType::OpenGL:
binding = new OpenGLBinding;
break;
case BackendType::Metal:
#if defined(__APPLE__)
binding = CreateMetalBinding();
#else
fprintf(stderr, "Metal backend not present on this platform\n");
#endif
break;
case BackendType::D3D12:
#if defined(_WIN32)
binding = CreateD3D12Binding();
#else
fprintf(stderr, "D3D12 backend not present on this platform\n");
#endif
break;
case BackendType::Null:
binding = new NullBinding;
break;
binding = utils::CreateBinding(backendType);
if (binding == nullptr) {
return nxt::Device();
}
if (!glfwInit()) {
@ -275,23 +198,27 @@ bool InitUtils(int argc, const char** argv) {
for (int i = 0; i < argc; i++) {
if (std::string("-b") == argv[i] || std::string("--backend") == argv[i]) {
i++;
if (i < argc && std::string("opengl") == argv[i]) {
backendType = BackendType::OpenGL;
if (i < argc && std::string("d3d12") == argv[i]) {
backendType = utils::BackendType::D3D12;
continue;
}
if (i < argc && std::string("metal") == argv[i]) {
backendType = BackendType::Metal;
continue;
}
if (i < argc && std::string("d3d12") == argv[i]) {
backendType = BackendType::D3D12;
backendType = utils::BackendType::Metal;
continue;
}
if (i < argc && std::string("null") == argv[i]) {
backendType = BackendType::Null;
backendType = utils::BackendType::Null;
continue;
}
fprintf(stderr, "--backend expects a backend name (opengl, metal, d3d12, null)\n");
if (i < argc && std::string("opengl") == argv[i]) {
backendType = utils::BackendType::OpenGL;
continue;
}
if (i < argc && std::string("vulkan") == argv[i]) {
backendType = utils::BackendType::Vulkan;
continue;
}
fprintf(stderr, "--backend expects a backend name (opengl, metal, d3d12, null, vulkan)\n");
return false;
}
if (std::string("-c") == argv[i] || std::string("--comand-buffer") == argv[i]) {
@ -309,7 +236,7 @@ bool InitUtils(int argc, const char** argv) {
}
if (std::string("-h") == argv[i] || std::string("--help") == argv[i]) {
printf("Usage: %s [-b BACKEND] [-c COMMAND_BUFFER]\n", argv[0]);
printf(" BACKEND is one of: opengl, metal, d3d12, null\n");
printf(" BACKEND is one of: d3d12, metal, null, opengl, vulkan\n");
printf(" COMMAND_BUFFER is one of: none, terrible\n");
return false;
}

View File

@ -13,6 +13,6 @@
# limitations under the License.
add_executable(glTFViewer glTFViewer.cpp)
target_link_libraries(glTFViewer utils)
target_link_libraries(glTFViewer sample_utils)
target_include_directories(glTFViewer PUBLIC ../ ${GLM_INCLUDE_DIR})
SetCXX14(glTFViewer)

View File

@ -0,0 +1,56 @@
// 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.
#include "BackendBinding.h"
namespace utils {
BackendBinding* CreateD3D12Binding();
BackendBinding* CreateMetalBinding();
BackendBinding* CreateOpenGLBinding();
BackendBinding* CreateNullBinding();
BackendBinding* CreateVulkanBinding();
void BackendBinding::SetWindow(GLFWwindow* window) {
this->window = window;
}
BackendBinding* CreateBinding(BackendType type) {
switch (type) {
case BackendType::D3D12:
#if defined(_WIN32)
return CreateD3D12Binding();
#else
return nullptr;
#endif
case BackendType::OpenGL:
return CreateOpenGLBinding();
case BackendType::Metal:
#if defined(__APPLE__)
return CreateMetalBinding();
#else
return nullptr;
#endif
case BackendType::Null:
return CreateNullBinding();
case BackendType::Vulkan:
return nullptr; // TODO(cwallez@chromium.org) change it to CreateVulkanBinding();
}
}
}

View File

@ -15,20 +15,33 @@
#ifndef UTILS_BACKENDBINDING_H_
#define UTILS_BACKENDBINDING_H_
#include <nxt/nxt.h>
struct GLFWwindow;
typedef struct nxtProcTable_s nxtProcTable;
typedef struct nxtDeviceImpl* nxtDevice;
class BackendBinding {
public:
virtual void SetupGLFWWindowHints() = 0;
virtual void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) = 0;
virtual void SwapBuffers() = 0;
namespace utils {
void SetWindow(GLFWwindow* window) {this->window = window;}
enum class BackendType {
D3D12,
Metal,
OpenGL,
Null,
Vulkan,
};
protected:
GLFWwindow* window = nullptr;
};
class BackendBinding {
public:
virtual void SetupGLFWWindowHints() = 0;
virtual void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) = 0;
virtual void SwapBuffers() = 0;
void SetWindow(GLFWwindow* window);
protected:
GLFWwindow* window = nullptr;
};
BackendBinding* CreateBinding(BackendType type);
}
#endif // UTILS_BACKENDBINDING_H_

39
src/utils/CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
# 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.
set(UTILS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND UTILS_SOURCES
${UTILS_DIR}/BackendBinding.cpp
${UTILS_DIR}/BackendBinding.h
${UTILS_DIR}/NullBinding.cpp
${UTILS_DIR}/OpenGLBinding.cpp
)
if (APPLE)
list(APPEND UTILS_SOURCES
${UTILS_DIR}/MetalBinding.mm
)
endif()
if (WIN32)
list(APPEND UTILS_SOURCES
${UTILS_DIR}/D3D12Binding.cpp
)
endif()
add_library(utils STATIC ${UTILS_SOURCES})
target_link_libraries(utils nxt_backend shaderc nxtcpp nxt)
target_include_directories(utils PUBLIC ${SRC_DIR})
SetCXX14(utils)

257
src/utils/D3D12Binding.cpp Normal file
View File

@ -0,0 +1,257 @@
// 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.
#include "BackendBinding.h"
#define GLFW_EXPOSE_NATIVE_WIN32
#include "GLFW/glfw3.h"
#include "GLFW/glfw3native.h"
#include <assert.h>
#include <wrl.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#define ASSERT assert
using Microsoft::WRL::ComPtr;
namespace backend {
namespace d3d12 {
void Init(ComPtr<ID3D12Device> d3d12Device, nxtProcTable* procs, nxtDevice* device);
ComPtr<ID3D12CommandQueue> GetCommandQueue(nxtDevice device);
void SetNextRenderTargetDescriptor(nxtDevice device, D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor);
uint64_t GetSerial(const nxtDevice device);
void NextSerial(nxtDevice device);
void ExecuteCommandLists(nxtDevice device, std::initializer_list<ID3D12CommandList*> commandLists);
void WaitForSerial(nxtDevice device, uint64_t serial);
void OpenCommandList(nxtDevice device, ComPtr<ID3D12GraphicsCommandList>* commandList);
}
}
namespace utils {
class D3D12Binding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
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 additional debug layers.
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
}
}
#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)
));
backend::d3d12::Init(d3d12Device, procs, device);
backendDevice = *device;
commandQueue = backend::d3d12::GetCommandQueue(backendDevice);
int width, height;
glfwGetWindowSize(window, &width, &height);
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.Width = width;
swapChainDesc.Height = height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = kFrameCount;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
HWND win32Window = glfwGetWin32Window(window);
ComPtr<IDXGISwapChain1> swapChain1;
ASSERT_SUCCESS(factory->CreateSwapChainForHwnd(
commandQueue.Get(),
win32Window,
&swapChainDesc,
nullptr,
nullptr,
&swapChain1
));
ASSERT_SUCCESS(swapChain1.As(&swapChain));
// Describe and create a render target view (RTV) descriptor heap.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = kFrameCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ASSERT_SUCCESS(d3d12Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap)));
rtvDescriptorSize = d3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// Create a RTV for each frame.
{
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart();
for (uint32_t n = 0; n < kFrameCount; ++n) {
ASSERT_SUCCESS(swapChain->GetBuffer(n, IID_PPV_ARGS(&renderTargetResources[n])));
d3d12Device->CreateRenderTargetView(renderTargetResources[n].Get(), nullptr, renderTargetViewHandle);
renderTargetViewHandle.ptr += rtvDescriptorSize;
}
}
// Get the initial render target and arbitrarily choose a "previous" render target that's different
previousRenderTargetIndex = renderTargetIndex = swapChain->GetCurrentBackBufferIndex();
previousRenderTargetIndex = renderTargetIndex == 0 ? 1 : 0;
// Initial the serial for all render targets
const uint64_t initialSerial = backend::d3d12::GetSerial(backendDevice);
for (uint32_t n = 0; n < kFrameCount; ++n) {
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());
ID3D12CommandList* commandLists[] = { commandList.Get() };
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
backend::d3d12::NextSerial(backendDevice);
}
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart();
renderTargetViewHandle.ptr += rtvDescriptorSize * renderTargetIndex;
backend::d3d12::SetNextRenderTargetDescriptor(backendDevice, renderTargetViewHandle);
}
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());
ID3D12CommandList* commandLists[] = { commandList.Get() };
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
}
ASSERT_SUCCESS(swapChain->Present(1, 0));
// Transition last frame's render target back to being a render target
{
backend::d3d12::OpenCommandList(backendDevice, &commandList);
D3D12_RESOURCE_BARRIER resourceBarrier;
resourceBarrier.Transition.pResource = renderTargetResources[previousRenderTargetIndex].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());
ID3D12CommandList* commandLists[] = { commandList.Get() };
backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() });
}
backend::d3d12::NextSerial(backendDevice);
previousRenderTargetIndex = renderTargetIndex;
renderTargetIndex = swapChain->GetCurrentBackBufferIndex();
// If the next render target is not ready to be rendered yet, wait until it is ready.
// If the last completed serial is less than the last requested serial for this render target,
// then the commands previously executed on this render target have not yet completed
backend::d3d12::WaitForSerial(backendDevice, lastSerialRenderTargetWasUsed[renderTargetIndex]);
lastSerialRenderTargetWasUsed[renderTargetIndex] = backend::d3d12::GetSerial(backendDevice);
// Tell the backend to render to the current render target
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart();
renderTargetViewHandle.ptr += rtvDescriptorSize * renderTargetIndex;
backend::d3d12::SetNextRenderTargetDescriptor(backendDevice, renderTargetViewHandle);
}
private:
nxtDevice backendDevice = nullptr;
static constexpr unsigned int kFrameCount = 2;
// Initialization
ComPtr<IDXGIFactory4> factory;
ComPtr<IDXGIAdapter1> hardwareAdapter;
ComPtr<ID3D12Device> d3d12Device;
ComPtr<ID3D12CommandQueue> commandQueue;
ComPtr<IDXGISwapChain3> swapChain;
ComPtr<ID3D12DescriptorHeap> renderTargetViewHeap;
ComPtr<ID3D12Resource> renderTargetResources[kFrameCount];
uint32_t rtvDescriptorSize;
// 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;
for (uint32_t adapterIndex = 0; ; ++adapterIndex) {
IDXGIAdapter1* adapter = nullptr;
if (factory->EnumAdapters1(adapterIndex, &adapter) == DXGI_ERROR_NOT_FOUND) {
break; // No more adapters to enumerate.
}
// Check to see if the adapter supports Direct3D 12, but don't create the actual device yet.
if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) {
*hardwareAdapter = adapter;
return true;
}
adapter->Release();
}
return false;
}
};
BackendBinding* CreateD3D12Binding() {
return new D3D12Binding;
}
}

82
src/utils/MetalBinding.mm Normal file
View File

@ -0,0 +1,82 @@
// 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.
#include "BackendBinding.h"
#define GLFW_EXPOSE_NATIVE_COCOA
#include "GLFW/glfw3.h"
#include "GLFW/glfw3native.h"
#import <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.h>
namespace backend {
namespace metal {
void Init(id<MTLDevice> metalDevice, nxtProcTable* procs, nxtDevice* device);
void SetNextDrawable(nxtDevice device, id<CAMetalDrawable> drawable);
void Present(nxtDevice device);
}
}
namespace utils {
class MetalBinding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}
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());
}
private:
id<CAMetalDrawable> GetNextDrawable() {
lastDrawable = [layer nextDrawable];
return lastDrawable;
}
id<MTLDevice> metalDevice = nil;
CAMetalLayer* layer = nullptr;
id<CAMetalDrawable> lastDrawable = nil;
nxtDevice backendDevice = nullptr;
};
BackendBinding* CreateMetalBinding() {
return new MetalBinding;
}
}

41
src/utils/NullBinding.cpp Normal file
View File

@ -0,0 +1,41 @@
// 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.
#include "BackendBinding.h"
namespace backend {
namespace null {
void Init(nxtProcTable* procs, nxtDevice* device);
}
}
namespace utils {
class NullBinding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
}
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
backend::null::Init(procs, device);
}
void SwapBuffers() override {
}
};
BackendBinding* CreateNullBinding() {
return new NullBinding;
}
}

View File

@ -0,0 +1,56 @@
// 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.
#include "BackendBinding.h"
#include "GLFW/glfw3.h"
namespace backend {
namespace opengl {
void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device);
void HACKCLEAR();
}
}
namespace utils {
class OpenGLBinding : public BackendBinding {
public:
void SetupGLFWWindowHints() override {
#ifdef __APPLE__
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#endif
}
void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
glfwMakeContextCurrent(window);
backend::opengl::Init(reinterpret_cast<void*(*)(const char*)>(glfwGetProcAddress), procs, device);
}
void SwapBuffers() override {
glfwSwapBuffers(window);
backend::opengl::HACKCLEAR();
}
};
BackendBinding* CreateOpenGLBinding() {
return new OpenGLBinding;
}
}