diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/file_utils.c | 68 | ||||
-rw-r--r-- | examples/file_utils.h | 41 | ||||
-rw-r--r-- | examples/pugl_shader_demo.c | 20 | ||||
-rw-r--r-- | examples/pugl_vulkan_cxx_demo.cpp | 53 | ||||
-rw-r--r-- | examples/shaders/header_330.glsl | 5 | ||||
-rw-r--r-- | examples/shaders/header_420.glsl | 5 | ||||
-rw-r--r-- | examples/shaders/rect.frag | 33 | ||||
-rw-r--r-- | examples/shaders/rect.vert | 35 |
8 files changed, 241 insertions, 19 deletions
diff --git a/examples/file_utils.c b/examples/file_utils.c new file mode 100644 index 0000000..2b00bdc --- /dev/null +++ b/examples/file_utils.c @@ -0,0 +1,68 @@ +/* + Copyright 2019-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#if !defined(__APPLE__) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +#endif + +#include "file_utils.h" + +#ifdef _WIN32 +# include <io.h> +# include <windows.h> +# define F_OK 0 +#else +# include <libgen.h> +# include <unistd.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char* +resourcePath(const char* const programPath, const char* const name) +{ + char* const binary = strdup(programPath); + +#ifdef _WIN32 + char programDir[_MAX_DIR]; + _splitpath(binary, programDir, NULL, NULL, NULL); + _splitpath(binary, NULL, programDir + strlen(programDir), NULL, NULL); + programDir[strlen(programDir) - 1] = '\0'; +#else + char* const programDir = dirname(binary); +#endif + + const size_t programDirLen = strlen(programDir); + const size_t nameLen = strlen(name); + const size_t totalLen = programDirLen + nameLen + 4; + + char* const programRelative = (char*)calloc(totalLen, 1); + snprintf(programRelative, totalLen, "%s/%s", programDir, name); + if (!access(programRelative, F_OK)) { + free(binary); + return programRelative; + } + + free(programRelative); + free(binary); + + const size_t sysPathLen = strlen(PUGL_DATA_DIR) + nameLen + 4; + char* const sysPath = (char*)calloc(sysPathLen, 1); + snprintf(sysPath, sysPathLen, "%s/%s", PUGL_DATA_DIR, name); + return sysPath; +} diff --git a/examples/file_utils.h b/examples/file_utils.h new file mode 100644 index 0000000..1530157 --- /dev/null +++ b/examples/file_utils.h @@ -0,0 +1,41 @@ +/* + Copyright 2019-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef EXAMPLES_FILE_UTILS_H +#define EXAMPLES_FILE_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return the path to a resource file. + + This takes a name like "shaders/something.glsl" and returns the actual + path that can be used to load that resource, which may be relative to the + current executable (for running in bundles or the build directory), or a + shared system directory for installs. + + The returned path must be freed with free(). +*/ +char* +resourcePath(const char* programPath, const char* name); + +#ifdef __cplusplus +} +#endif + +#endif // EXAMPLES_FILE_UTILS_H diff --git a/examples/pugl_shader_demo.c b/examples/pugl_shader_demo.c index 800b8f0..8ebbe60 100644 --- a/examples/pugl_shader_demo.c +++ b/examples/pugl_shader_demo.c @@ -35,6 +35,7 @@ */ #include "demo_utils.h" +#include "file_utils.h" #include "rects.h" #include "shader_utils.h" #include "test/test_utils.h" @@ -62,6 +63,7 @@ typedef struct typedef struct { + const char* programPath; PuglWorld* world; PuglView* view; PuglTestOptions opts; @@ -203,14 +205,18 @@ makeRects(const size_t numRects) } static char* -loadShader(const char* const path) +loadShader(const char* const programPath, const char* const name) { + char* const path = resourcePath(programPath, name); + fprintf(stderr, "Loading shader %s\n", path); + FILE* const file = fopen(path, "r"); if (!file) { logError("Failed to open '%s'\n", path); return NULL; } + free(path); fseek(file, 0, SEEK_END); const size_t fileSize = (size_t)ftell(file); @@ -303,9 +309,14 @@ setupGl(PuglTestApp* app) : "shaders/header_420.glsl"); // Load shader sources - char* const headerSource = loadShader(headerFile); - char* const vertexSource = loadShader("shaders/rect.vert"); - char* const fragmentSource = loadShader("shaders/rect.frag"); + char* const headerSource = loadShader(app->programPath, headerFile); + + char* const vertexSource = loadShader(app->programPath, + "shaders/rect.vert"); + + char* const fragmentSource = loadShader(app->programPath, + "shaders/rect.frag"); + if (!vertexSource || !fragmentSource) { logError("Failed to load shader sources\n"); return PUGL_FAILURE; @@ -405,6 +416,7 @@ main(int argc, char** argv) { PuglTestApp app = {0}; + app.programPath = argv[0]; app.glMajorVersion = 3; app.glMinorVersion = 3; diff --git a/examples/pugl_vulkan_cxx_demo.cpp b/examples/pugl_vulkan_cxx_demo.cpp index 0a86d16..21fd4df 100644 --- a/examples/pugl_vulkan_cxx_demo.cpp +++ b/examples/pugl_vulkan_cxx_demo.cpp @@ -28,6 +28,7 @@ */ #include "demo_utils.h" +#include "file_utils.h" #include "rects.h" #include "test/test_utils.h" @@ -150,7 +151,9 @@ struct RectData { /// Shader modules for drawing rectangles struct RectShaders { - VkResult init(const sk::VulkanApi& vk, const GraphicsDevice& gpu); + VkResult init(const sk::VulkanApi& vk, + const GraphicsDevice& gpu, + const std::string& programPath); sk::ShaderModule vert{}; sk::ShaderModule frag{}; @@ -687,9 +690,15 @@ RenderPass::init(const sk::VulkanApi& vk, } std::vector<uint32_t> -readFile(const std::string& filename) +readFile(const char* const programPath, const std::string& filename) { - std::unique_ptr<FILE, decltype(&fclose)> file{fopen(filename.c_str(), "rb"), + std::unique_ptr<char, decltype(&free)> path{resourcePath(programPath, + filename.c_str()), + &free}; + + std::cerr << "Loading shader: " << path.get() << std::endl; + + std::unique_ptr<FILE, decltype(&fclose)> file{fopen(path.get(), "rb"), &fclose}; if (!file) { @@ -726,10 +735,15 @@ createShaderModule(const sk::VulkanApi& vk, } VkResult -RectShaders::init(const sk::VulkanApi& vk, const GraphicsDevice& gpu) +RectShaders::init(const sk::VulkanApi& vk, + const GraphicsDevice& gpu, + const std::string& programPath) { - auto vertShaderCode = readFile("build/shaders/rect.vert.spv"); - auto fragShaderCode = readFile("build/shaders/rect.frag.spv"); + auto vertShaderCode = readFile(programPath.c_str(), + "shaders/rect.vert.spv"); + + auto fragShaderCode = readFile(programPath.c_str(), + "shaders/rect.frag.spv"); if (vertShaderCode.empty() || fragShaderCode.empty()) { return VK_ERROR_INITIALIZATION_FAILED; @@ -1407,8 +1421,11 @@ private: class PuglVulkanDemo { public: - PuglVulkanDemo(const PuglTestOptions& o, size_t numRects); + PuglVulkanDemo(const char* executablePath, + const PuglTestOptions& o, + size_t numRects); + const char* programPath; PuglTestOptions opts; pugl::World world; pugl::VulkanLoader loader; @@ -1436,8 +1453,11 @@ makeRects(const size_t numRects, const uint32_t windowWidth) return rects; } -PuglVulkanDemo::PuglVulkanDemo(const PuglTestOptions& o, const size_t numRects) - : opts{o} +PuglVulkanDemo::PuglVulkanDemo(const char* const executablePath, + const PuglTestOptions& o, + const size_t numRects) + : programPath{executablePath} + , opts{o} , world{pugl::WorldType::program, pugl::WorldFlag::threads} , loader{world} , view{world, *this} @@ -1732,9 +1752,11 @@ VulkanContext::init(pugl::VulkanLoader& loader, const PuglTestOptions& opts) } int -run(const PuglTestOptions opts, const size_t numRects) +run(const char* const programPath, + const PuglTestOptions opts, + const size_t numRects) { - PuglVulkanDemo app{opts, numRects}; + PuglVulkanDemo app{programPath, opts, numRects}; VkResult r = VK_SUCCESS; const auto width = static_cast<int>(app.extent.width); @@ -1778,7 +1800,7 @@ run(const PuglTestOptions opts, const size_t numRects) } // Load shader modules - if ((r = app.rectShaders.init(vk, app.gpu))) { + if ((r = app.rectShaders.init(vk, app.gpu, app.programPath))) { return logError("Failed to load shaders (%s)\n", sk::string(r)); } @@ -1826,9 +1848,10 @@ int main(int argc, char** argv) { // Parse command line options - const PuglTestOptions opts = puglParseTestOptions(&argc, &argv); + const char* const programPath = argv[0]; + const PuglTestOptions opts = puglParseTestOptions(&argc, &argv); if (opts.help) { - puglPrintTestUsage(argv[0], ""); + puglPrintTestUsage(programPath, ""); return 0; } @@ -1844,5 +1867,5 @@ main(int argc, char** argv) } // Run application - return run(opts, static_cast<size_t>(numRects)); + return run(programPath, opts, static_cast<size_t>(numRects)); } diff --git a/examples/shaders/header_330.glsl b/examples/shaders/header_330.glsl new file mode 100644 index 0000000..bfe7a00 --- /dev/null +++ b/examples/shaders/header_330.glsl @@ -0,0 +1,5 @@ +#version 330 core + +#define INTER(qualifiers) +#define UBO(qualifiers) layout(std140) + diff --git a/examples/shaders/header_420.glsl b/examples/shaders/header_420.glsl new file mode 100644 index 0000000..55fbe8a --- /dev/null +++ b/examples/shaders/header_420.glsl @@ -0,0 +1,5 @@ +#version 420 core + +#define INTER(qualifiers) layout(qualifiers) +#define UBO(qualifiers) layout(std140, qualifiers) + diff --git a/examples/shaders/rect.frag b/examples/shaders/rect.frag new file mode 100644 index 0000000..ecec50d --- /dev/null +++ b/examples/shaders/rect.frag @@ -0,0 +1,33 @@ +/* The fragment shader uses the UV coordinates to calculate whether it is in + the T, R, B, or L border. These are then mixed with the border color, and + their inverse is mixed with the fill color, to calculate the fragment color. + For example, if we are in the top border, then T=1, so the border mix factor + TRBL=1, and the fill mix factor (1-TRBL) is 0. + + The use of pixel units here is handy because the border width can be + specified precisely in pixels to draw sharp lines. The border width is just + hardcoded, but could be made a uniform or vertex attribute easily enough. */ + +INTER(location = 0) noperspective in vec2 f_uv; +INTER(location = 1) noperspective in vec2 f_size; +INTER(location = 2) noperspective in vec4 f_fillColor; + +layout(location = 0) out vec4 FragColor; + +void +main() +{ + const float borderWidth = 2.0; + + vec4 borderColor = f_fillColor + vec4(0.0, 0.4, 0.4, 0.0); + float t = step(borderWidth, f_uv[1]); + float r = step(borderWidth, f_size.x - f_uv[0]); + float b = step(borderWidth, f_size.y - f_uv[1]); + float l = step(borderWidth, f_uv[0]); + float fillMix = t * r * b * l; + float borderMix = 1.0 - fillMix; + vec4 fill = fillMix * f_fillColor; + vec4 border = borderMix * borderColor; + + FragColor = fill + border; +} diff --git a/examples/shaders/rect.vert b/examples/shaders/rect.vert new file mode 100644 index 0000000..09f1917 --- /dev/null +++ b/examples/shaders/rect.vert @@ -0,0 +1,35 @@ +/* The vertex shader is trivial, but forwards scaled UV coordinates (in pixels) + to the fragment shader for drawing the border. */ + +UBO(binding = 0) uniform UniformBufferObject +{ + mat4 projection; +} ubo; + +layout(location = 0) in vec2 v_position; +layout(location = 1) in vec2 v_origin; +layout(location = 2) in vec2 v_size; +layout(location = 3) in vec4 v_fillColor; + +INTER(location = 0) noperspective out vec2 f_uv; +INTER(location = 1) noperspective out vec2 f_size; +INTER(location = 2) noperspective out vec4 f_fillColor; + +void +main() +{ + // clang-format off + mat4 m = mat4(v_size[0], 0.0, 0.0, 0.0, + 0.0, v_size[1], 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + v_origin[0], v_origin[1], 0.0, 1.0); + // clang-format on + + mat4 MVP = ubo.projection * m; + + f_uv = v_position * v_size; + f_size = v_size; + f_fillColor = v_fillColor; + + gl_Position = MVP * vec4(v_position, 0.0, 1.0); +} |