aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/file_utils.c68
-rw-r--r--examples/file_utils.h41
-rw-r--r--examples/pugl_shader_demo.c20
-rw-r--r--examples/pugl_vulkan_cxx_demo.cpp53
-rw-r--r--examples/shaders/header_330.glsl5
-rw-r--r--examples/shaders/header_420.glsl5
-rw-r--r--examples/shaders/rect.frag33
-rw-r--r--examples/shaders/rect.vert35
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);
+}