// 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 #include #include #include "GLFW/glfw3.h" #include "BackendBinding.h" #include "../src/wire/TerribleCommandBuffer.h" #include #include #include #include #ifdef _WIN32 #include #else #include #endif BackendBinding* CreateMetalBinding(); 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(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, Null, }; enum class CmdBufType { None, Terrible, //TODO(cwallez@chromium.org) double terrible cmdbuf }; static BackendType backendType = BackendType::OpenGL; static CmdBufType cmdBufType = CmdBufType::Terrible; static BackendBinding* binding = nullptr; static GLFWwindow* window = nullptr; static nxt::wire::CommandHandler* wireServer = nullptr; static nxt::wire::CommandHandler* wireClient = nullptr; 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 no present on this platform\n"); #endif break; case BackendType::Null: binding = new NullBinding; break; } if (!glfwInit()) { return nxt::Device(); } binding->SetupGLFWWindowHints(); window = glfwCreateWindow(640, 480, "NXT window", nullptr, nullptr); if (!window) { return nxt::Device(); } binding->SetWindow(window); nxtDevice backendDevice; nxtProcTable backendProcs; binding->GetProcAndDevice(&backendProcs, &backendDevice); nxtDevice cDevice = nullptr; nxtProcTable procs; switch (cmdBufType) { case CmdBufType::None: procs = backendProcs; cDevice = backendDevice; break; case CmdBufType::Terrible: { c2sBuf = new nxt::wire::TerribleCommandBuffer(); s2cBuf = new nxt::wire::TerribleCommandBuffer(); wireServer = nxt::wire::NewServerCommandHandler(backendDevice, backendProcs, s2cBuf); c2sBuf->SetHandler(wireServer); nxtDevice clientDevice; nxtProcTable clientProcs; wireClient = nxt::wire::NewClientDevice(&clientProcs, &clientDevice, c2sBuf); s2cBuf->SetHandler(wireClient); procs = clientProcs; cDevice = clientDevice; } break; } nxtSetProcs(&procs); procs.deviceSetErrorCallback(cDevice, PrintDeviceError, 0); return nxt::Device::Acquire(cDevice); } nxt::ShaderModule CreateShaderModule(const nxt::Device& device, nxt::ShaderStage stage, const char* source) { shaderc::Compiler compiler; shaderc::CompileOptions options; shaderc_shader_kind kind; switch (stage) { case nxt::ShaderStage::Vertex: kind = shaderc_glsl_vertex_shader; break; case nxt::ShaderStage::Fragment: kind = shaderc_glsl_fragment_shader; break; case nxt::ShaderStage::Compute: kind = shaderc_glsl_compute_shader; break; } auto result = compiler.CompileGlslToSpv(source, strlen(source), kind, "myshader?", options); if (result.GetCompilationStatus() != shaderc_compilation_status_success) { std::cerr << result.GetErrorMessage(); return {}; } size_t size = (result.cend() - result.cbegin()); #ifdef DUMP_SPIRV_ASSEMBLY { auto resultAsm = compiler.CompileGlslToSpvAssembly(source, strlen(source), kind, "myshader?", options); size_t sizeAsm = (resultAsm.cend() - resultAsm.cbegin()); char* buffer = reinterpret_cast(malloc(sizeAsm + 1)); memcpy(buffer, resultAsm.cbegin(), sizeAsm); buffer[sizeAsm] = '\0'; printf("SPIRV ASSEMBLY DUMP START\n%s\nSPIRV ASSEMBLY DUMP END\n", buffer); free(buffer); } #endif #ifdef DUMP_SPIRV_JS_ARRAY printf("SPIRV JS ARRAY DUMP START\n"); for (size_t i = 0; i < size; i++) { printf("%#010x", result.cbegin()[i]); if ((i + 1) % 4 == 0) { printf(",\n"); } else { printf(", "); } } printf("\n"); printf("SPIRV JS ARRAY DUMP END\n"); #endif return device.CreateShaderModuleBuilder() .SetSource(size, result.cbegin()) .GetResult(); } void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer) { *renderPass = device.CreateRenderPassBuilder() .SetAttachmentCount(1) .AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm) .SetSubpassCount(1) .SubpassSetColorAttachment(0, 0, 0) .GetResult(); *framebuffer = device.CreateFramebufferBuilder() .SetRenderPass(*renderPass) .SetDimensions(640, 480) .GetResult(); } nxt::Buffer CreateFrozenBufferFromData(const nxt::Device& device, void* data, uint32_t size, nxt::BufferUsageBit usage) { nxt::Buffer buffer = device.CreateBufferBuilder() .SetAllowedUsage(nxt::BufferUsageBit::Mapped | usage) .SetInitialUsage(nxt::BufferUsageBit::Mapped) .SetSize(size) .GetResult(); buffer.SetSubData(0, size / sizeof(uint32_t), reinterpret_cast(data)); buffer.FreezeUsage(usage); return buffer; } extern "C" { 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; continue; } if (i < argc && std::string("metal") == argv[i]) { backendType = BackendType::Metal; continue; } if (i < argc && std::string("null") == argv[i]) { backendType = BackendType::Null; continue; } fprintf(stderr, "--backend expects a backend name (opengl, metal, null)\n"); return false; } if (std::string("-c") == argv[i] || std::string("--comand-buffer") == argv[i]) { i++; if (i < argc && std::string("none") == argv[i]) { cmdBufType = CmdBufType::None; continue; } if (i < argc && std::string("terrible") == argv[i]) { cmdBufType = CmdBufType::Terrible; continue; } fprintf(stderr, "--command-buffer expects a command buffer name (none, terrible)\n"); return false; } 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, null\n"); printf(" COMMAND_BUFFER is one of: none, terrible\n"); return false; } } return true; } nxtDevice CreateNXTDevice() { return CreateCppNXTDevice().Release(); } nxtShaderModule CreateShaderModule(nxtDevice device, nxtShaderStage stage, const char* source) { return CreateShaderModule(device, static_cast(stage), source).Release(); } nxtBuffer CreateFrozenBufferFromData(nxtDevice device, void* data, uint32_t size, nxtBufferUsageBit usage) { return CreateFrozenBufferFromData(device, data, size, usage); } void DoSwapBuffers() { if (cmdBufType == CmdBufType::Terrible) { c2sBuf->Flush(); s2cBuf->Flush(); } glfwPollEvents(); binding->SwapBuffers(); } #ifdef _WIN32 void USleep(uint64_t usecs) { Sleep(usecs / 1000); } #else void USleep(uint64_t usecs) { usleep(usecs); } #endif bool ShouldQuit() { return glfwWindowShouldClose(window); } GLFWwindow* GetGLFWWindow() { return window; } }