// 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 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(); } }; void PrintDeviceError(const char* message, nxt::CallbackUserdata) { std::cout << "Device error: " << message << std::endl; } enum class BackendType { OpenGL, Metal, }; 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; void GetProcTableAndDevice(nxtProcTable* procs, nxt::Device* device) { 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; } if (!glfwInit()) { return; } binding->SetupGLFWWindowHints(); window = glfwCreateWindow(640, 480, "NXT window", nullptr, nullptr); if (!window) { return; } binding->SetWindow(window); nxtDevice backendDevice; nxtProcTable backendProcs; binding->GetProcAndDevice(&backendProcs, &backendDevice); switch (cmdBufType) { case CmdBufType::None: *procs = backendProcs; *device = nxt::Device::Acquire(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; *device = nxt::Device::Acquire(clientDevice); } break; } procs->deviceSetErrorCallback(device->Get(), PrintDeviceError, 0); } 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(); } 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; } fprintf(stderr, "--backend expects a backend name (opengl, metal)\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\n"); printf(" COMMAND_BUFFER is one of: none, terrible\n"); return false; } } return true; } void GetProcTableAndDevice(nxtProcTable* procs, nxtDevice* device) { nxt::Device cppDevice; GetProcTableAndDevice(procs, &cppDevice); *device = cppDevice.Release(); } nxtShaderModule CreateShaderModule(nxtDevice device, nxtShaderStage stage, const char* source) { return CreateShaderModule(device, static_cast(stage), source).Release(); } void SwapBuffers() { if (cmdBufType == CmdBufType::Terrible) { c2sBuf->Flush(); s2cBuf->Flush(); } glfwPollEvents(); binding->SwapBuffers(); } bool ShouldQuit() { return glfwWindowShouldClose(window); } GLFWwindow* GetWindow() { return window; } }