Last active
December 22, 2018 20:05
-
-
Save volcanoauthors/bcb31e07f46023a6b95acf7e16283aab to your computer and use it in GitHub Desktop.
Cheat sheet: triangle.cpp and BUILD.gn patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/BUILD.gn b/BUILD.gn | |
index a095b4c..102246d 100644 | |
--- a/BUILD.gn | |
+++ b/BUILD.gn | |
@@ -1,6 +1,22 @@ | |
# Copyright (c) 2017-2018 the Volcano Authors. Licensed under GPLv3. | |
import("//src/gn/vendor/glslangValidator.gni") | |
+glslangVulkanToHeader("triangle_shaders") { | |
+ sources = [ | |
+ "triangle.vert", # compile to bytecode available at "triangle.vert.h" | |
+ "triangle.frag", # compile to bytecode available at "triangle.frag.h" | |
+ ] | |
+} | |
+ | |
+executable("triangle") { | |
+ sources = [ "triangle.cpp" ] | |
+ deps = [ | |
+ ":triangle_shaders", # Tells the compiler where to find triangle.vert.h | |
+ "//src/gn/vendor/glfw", | |
+ "//vendor/volcano", | |
+ ] | |
+} | |
+ | |
# If these samples build ok, then assume all samples will build ok. | |
group("build_first") { | |
deps = [ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define GLFW_INCLUDE_VULKAN | |
#include <GLFW/glfw3.h> | |
#ifdef _WIN32 | |
#define GLFW_EXPOSE_NATIVE_WIN32 | |
#include <GLFW/glfw3native.h> | |
#endif /* _WIN32 */ | |
#include <src/language/language.h> | |
#include <src/science/science.h> | |
#include "triangle.frag.h" | |
#include "triangle.vert.h" | |
// pipe0 is the Vulkan Pipeline. | |
std::shared_ptr<command::Pipeline> pipe0; | |
// cmdBuffers are a vector because there is one per framebuffer. | |
std::vector<command::CommandBuffer> cmdBuffers; | |
static void handleGlfwErrors(int error, const char* description) { | |
printf("glfw error %d: %s\n", error, description); | |
} | |
// Wrap the function glfwCreateWindowSurface for Instance::ctorError(): | |
static VkResult handleCreateSurface(language::Instance& inst, void* window) | |
{ | |
return glfwCreateWindowSurface(inst.vk, (GLFWwindow*) window, nullptr, | |
&inst.surface); | |
} | |
const int WIDTH = 800, HEIGHT = 600; | |
int buildFramebuf(void* containerPtr, language::Framebuf& framebuf, | |
size_t framebuf_i, size_t /*poolQindex*/) { | |
science::CommandPoolContainer& cpoolContainer( | |
*static_cast<science::CommandPoolContainer*>(containerPtr)); | |
if (framebuf_i == 0) { // Do this only for the first frame buffer. | |
if (cpoolContainer.cpool.updateBuffersAndPass(cmdBuffers, | |
cpoolContainer.pass)) { | |
return 1; | |
} | |
} | |
// Write drawing commands for each framebuf_i. | |
auto& cmdBuffer = cmdBuffers.at(framebuf_i); | |
if (cmdBuffer.beginSimultaneousUse() || | |
// Begin RenderPass. | |
cmdBuffer.beginPrimaryPass(cpoolContainer.pass, framebuf) || | |
cmdBuffer.bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipe0) || | |
cmdBuffer.draw(3, 1, 0, 0) || | |
// End RenderPass. | |
cmdBuffer.endRenderPass() || cmdBuffer.end()) { | |
logE("buildFramebuf: cmdBuffer [%zu] failed\n", framebuf_i); | |
return 1; | |
} | |
return 0; | |
} | |
int mainLoop(GLFWwindow* window) { | |
unsigned int extCount = 0; | |
const char** exts = glfwGetRequiredInstanceExtensions(&extCount); | |
language::Instance inst; | |
for (unsigned int i = 0; i < extCount; i++) { | |
inst.requiredExtensions.push_back(exts[i]); | |
} | |
if (inst.ctorError(handleCreateSurface, window)) { | |
return 1; | |
} | |
if (inst.open({WIDTH, HEIGHT})) { | |
return 1; | |
} | |
// inst has the logical device after inst.open(). | |
language::Device& dev = *inst.devs.at(0); | |
// CommandPoolContainer can allocate command pools from dev. | |
science::CommandPoolContainer cpoolContainer(dev); | |
cpoolContainer.resizeFramebufListeners.emplace_back( | |
std::make_pair(buildFramebuf, &cpoolContainer)); | |
pipe0 = cpoolContainer.pass.addPipeline(); | |
// Set viewport. | |
auto& extent = dev.swapChainInfo.imageExtent; | |
VkViewport& viewport = pipe0->info.viewports.at(0); | |
viewport.width = (float)extent.width; | |
viewport.height = (float)extent.height; | |
// Set scissors. | |
pipe0->info.scissors.at(0).extent = extent; | |
auto vshader = std::shared_ptr<command::Shader>(new command::Shader{dev}); | |
auto fshader = std::shared_ptr<command::Shader>(new command::Shader{dev}); | |
science::PresentSemaphore renderSemaphore(cpoolContainer); | |
command::Semaphore imageAvailableSemaphore(dev); | |
if (cpoolContainer.cpool.ctorError() || imageAvailableSemaphore.ctorError() || | |
renderSemaphore.ctorError() || | |
// Read compiled-in shaders from app into Vulkan. | |
vshader->loadSPV(spv_triangle_vert, sizeof(spv_triangle_vert)) || | |
fshader->loadSPV(spv_triangle_frag, sizeof(spv_triangle_frag)) || | |
// Add shader objects to pipeline. | |
pipe0->info.addShader(vshader, VK_SHADER_STAGE_VERTEX_BIT) || | |
pipe0->info.addShader(fshader, VK_SHADER_STAGE_FRAGMENT_BIT)) { | |
return 1; | |
} | |
// onResized will call buildFramebuf for each element of cmdBuffers. | |
// This is not fully set up for resizing yet, but has most of the pieces. | |
glfwSetWindowAttrib(window, GLFW_RESIZABLE, false); | |
if (cpoolContainer.onResized(dev.swapChainInfo.imageExtent, | |
memory::ASSUME_POOL_QINDEX)) { | |
return 1; | |
} | |
// Begin main loop. | |
unsigned frameCount = 0; | |
while (!glfwWindowShouldClose(window)) { | |
glfwPollEvents(); | |
frameCount++; | |
// https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#vkAcquireNextImageKHR | |
// Acquire an image index that can be rendered as soon as | |
// imageAvailableSemaphore is signaled. | |
uint32_t next_image_i; | |
if (cpoolContainer.acquireNextImage(frameCount, &next_image_i, | |
imageAvailableSemaphore)) { | |
return 1; | |
} | |
if (next_image_i != (uint32_t)-1) { | |
if (cmdBuffers.at(next_image_i) | |
.submit(memory::ASSUME_POOL_QINDEX, | |
{imageAvailableSemaphore.vk}, | |
{VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}, | |
{renderSemaphore.vk}) || | |
renderSemaphore.present(&next_image_i)) { | |
return 1; | |
} | |
} | |
// renderSemaphore.present() can set next_image_i == (uint32_t)-1 | |
if (next_image_i != (uint32_t)-1) { | |
// waitIdle() here is only to let any validation layers | |
// clean up resources that might otherwise leak. | |
if ((frameCount % 64) == 63 && renderSemaphore.waitIdle()) { | |
return 1; | |
} | |
} | |
} | |
if (cpoolContainer.cpool.deviceWaitIdle()) { | |
return 1; | |
} | |
// Destroy Pipeline before other objects that it depends on. | |
pipe0.reset(); | |
return 0; | |
} | |
int crossPlatformMain() { | |
glfwSetErrorCallback(handleGlfwErrors); | |
glfwInit(); | |
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "My Title", | |
nullptr /*monitor for fullscreen*/, nullptr /*context object sharing*/); | |
if (!window) { | |
return 1; | |
} | |
int r = mainLoop(window); | |
glfwDestroyWindow(window); | |
glfwTerminate(); | |
return r; | |
} | |
#ifdef _WIN32 | |
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { | |
return crossPlatformMain(); | |
} | |
#elif defined(__ANDROID__) | |
void android_main(android_app* app) { | |
(void)crossPlatformMain(); | |
} | |
#else | |
int main() { | |
return crossPlatformMain(); | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment