blob: b557f768dec0708bef194f43b62043c7ea2c05f8 [file] [log] [blame]
Corentin Wallezf07e3bd2017-04-20 14:38:20 -04001// Copyright 2017 The NXT Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <nxt/nxt.h>
16#include <nxt/nxtcpp.h>
17#include <shaderc/shaderc.hpp>
18#include "GLFW/glfw3.h"
19
20#include "BackendBinding.h"
21#include "../src/wire/TerribleCommandBuffer.h"
22
23#include <cstring>
24#include <iostream>
25#include <sstream>
26#include <iomanip>
27
28BackendBinding* CreateMetalBinding();
29
30namespace backend {
31 void RegisterSynchronousErrorCallback(nxtDevice device, void(*)(const char*, void*), void* userData);
32
33 namespace opengl {
34 void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device);
35 void HACKCLEAR();
36 }
37}
38
39class OpenGLBinding : public BackendBinding {
40 public:
41 void SetupGLFWWindowHints() override {
42 #ifdef __APPLE__
43 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
44 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
45 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
46 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
47 #else
48 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
49 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
50 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
51 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
52 #endif
53 }
54 void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override {
55 glfwMakeContextCurrent(window);
56 backend::opengl::Init(reinterpret_cast<void*(*)(const char*)>(glfwGetProcAddress), procs, device);
57 }
58 void SwapBuffers() override {
59 glfwSwapBuffers(window);
60 backend::opengl::HACKCLEAR();
61 }
62};
63
64enum class BackendType {
65 OpenGL,
66 Metal,
67};
68
69enum class CmdBufType {
70 None,
71 Terrible,
72};
73
74static BackendType backendType = BackendType::OpenGL;
75static CmdBufType cmdBufType = CmdBufType::Terrible;
76static BackendBinding* binding = nullptr;
77
78static GLFWwindow* window = nullptr;
79
80static nxt::wire::CommandHandler* wireServer = nullptr;
81static nxt::wire::TerribleCommandBuffer* cmdBuf = nullptr;
82
83void HandleSynchronousError(const char* errorMessage, void* userData) {
84 std::cerr << errorMessage << std::endl;
85
86 if (userData != nullptr) {
87 auto wireServer = reinterpret_cast<nxt::wire::CommandHandler*>(userData);
88 wireServer->OnSynchronousError();
89 }
90}
91
92void GetProcTableAndDevice(nxtProcTable* procs, nxt::Device* device) {
93 switch (backendType) {
94 case BackendType::OpenGL:
95 binding = new OpenGLBinding;
96 break;
97 case BackendType::Metal:
98 #if defined(__APPLE__)
99 binding = CreateMetalBinding();
100 #else
101 fprintf(stderr, "Metal backend no present on this platform\n");
102 #endif
103 break;
104 }
105
106 if (!glfwInit()) {
107 return;
108 }
109
110 binding->SetupGLFWWindowHints();
111 window = glfwCreateWindow(640, 480, "NXT window", nullptr, nullptr);
112 if (!window) {
113 return;
114 }
115
116 binding->SetWindow(window);
117
118 nxtDevice backendDevice;
119 nxtProcTable backendProcs;
120 binding->GetProcAndDevice(&backendProcs, &backendDevice);
121
122 switch (cmdBufType) {
123 case CmdBufType::None:
124 *procs = backendProcs;
125 *device = nxt::Device::Acquire(backendDevice);
126 break;
127
128 case CmdBufType::Terrible:
129 {
130 wireServer = nxt::wire::CreateCommandHandler(backendDevice, backendProcs);
131 cmdBuf = new nxt::wire::TerribleCommandBuffer(wireServer);
132
133 nxtDevice clientDevice;
134 nxtProcTable clientProcs;
135 nxt::wire::NewClientDevice(&clientProcs, &clientDevice, cmdBuf);
136
137 *procs = clientProcs;
138 *device = nxt::Device::Acquire(clientDevice);
139 }
140 break;
141 }
142
143 backend::RegisterSynchronousErrorCallback(backendDevice, HandleSynchronousError, wireServer);
144}
145
146nxt::ShaderModule CreateShaderModule(const nxt::Device& device, nxt::ShaderStage stage, const char* source) {
147 shaderc::Compiler compiler;
148 shaderc::CompileOptions options;
149
150 shaderc_shader_kind kind;
151 switch (stage) {
152 case nxt::ShaderStage::Vertex:
153 kind = shaderc_glsl_vertex_shader;
154 break;
155 case nxt::ShaderStage::Fragment:
156 kind = shaderc_glsl_fragment_shader;
157 break;
158 case nxt::ShaderStage::Compute:
159 kind = shaderc_glsl_compute_shader;
160 break;
161 }
162
163 auto result = compiler.CompileGlslToSpv(source, strlen(source), kind, "myshader?", options);
164 if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
165 std::cerr << result.GetErrorMessage();
166 return {};
167 }
168
169 size_t size = (result.cend() - result.cbegin());
170
171#ifdef DUMP_SPIRV_ASSEMBLY
172 {
173 auto resultAsm = compiler.CompileGlslToSpvAssembly(source, strlen(source), kind, "myshader?", options);
174 size_t sizeAsm = (resultAsm.cend() - resultAsm.cbegin());
175
176 char* buffer = reinterpret_cast<char*>(malloc(sizeAsm + 1));
177 memcpy(buffer, resultAsm.cbegin(), sizeAsm);
178 buffer[sizeAsm] = '\0';
179 printf("SPIRV ASSEMBLY DUMP START\n%s\nSPIRV ASSEMBLY DUMP END\n", buffer);
180 free(buffer);
181 }
182#endif
183
184#ifdef DUMP_SPIRV_JS_ARRAY
185 printf("SPIRV JS ARRAY DUMP START\n");
186 for (size_t i = 0; i < size; i++) {
187 printf("%#010x", result.cbegin()[i]);
188 if ((i + 1) % 4 == 0) {
189 printf(",\n");
190 } else {
191 printf(", ");
192 }
193 }
194 printf("\n");
195 printf("SPIRV JS ARRAY DUMP END\n");
196#endif
197
198 return device.CreateShaderModuleBuilder()
199 .SetSource(size, result.cbegin())
200 .GetResult();
201}
202
203extern "C" {
204 bool InitUtils(int argc, const char** argv) {
205 for (int i = 0; i < argc; i++) {
206 if (std::string("-b") == argv[i] || std::string("--backend") == argv[i]) {
207 i++;
208 if (i < argc && std::string("opengl") == argv[i]) {
209 backendType = BackendType::OpenGL;
210 continue;
211 }
212 if (i < argc && std::string("metal") == argv[i]) {
213 backendType = BackendType::Metal;
214 continue;
215 }
216 fprintf(stderr, "--backend expects a backend name (opengl, metal)\n");
217 return false;
218 }
219 if (std::string("-c") == argv[i] || std::string("--comand-buffer") == argv[i]) {
220 i++;
221 if (i < argc && std::string("none") == argv[i]) {
222 cmdBufType = CmdBufType::None;
223 continue;
224 }
225 if (i < argc && std::string("terrible") == argv[i]) {
226 cmdBufType = CmdBufType::Terrible;
227 continue;
228 }
229 fprintf(stderr, "--command-buffer expects a command buffer name (none, terrible)\n");
230 return false;
231 }
232 if (std::string("-h") == argv[i] || std::string("--help") == argv[i]) {
233 printf("Usage: %s [-b BACKEND] [-c COMMAND_BUFFER]\n", argv[0]);
234 printf(" BACKEND is one of: opengl, metal\n");
235 printf(" COMMAND_BUFFER is one of: none, terrible\n");
236 return false;
237 }
238 }
239 return true;
240 }
241
242 void GetProcTableAndDevice(nxtProcTable* procs, nxtDevice* device) {
243 nxt::Device cppDevice;
244 GetProcTableAndDevice(procs, &cppDevice);
245 *device = cppDevice.Release();
246 }
247
248 nxtShaderModule CreateShaderModule(nxtDevice device, nxtShaderStage stage, const char* source) {
249 return CreateShaderModule(device, static_cast<nxt::ShaderStage>(stage), source).Release();
250 }
251
252 void SwapBuffers() {
253 if (cmdBuf) {
254 cmdBuf->Flush();
255 }
256 glfwPollEvents();
257 binding->SwapBuffers();
258 }
259
260 bool ShouldQuit() {
261 return glfwWindowShouldClose(window);
262 }
263
264 GLFWwindow* GetWindow() {
265 return window;
266 }
267}