Skip to content

Commit

Permalink
debugged proper validation
Browse files Browse the repository at this point in the history
  • Loading branch information
PaoloMazzon committed Jul 23, 2024
1 parent fa07998 commit eb8009e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 47 deletions.
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Features

+ Simple and intuitive API built on top of SDL
+ Draw shapes/textures/3D models/arbitrary polygons to the screen or other textures
+ Fast, built with Vulkan 1.2 and doesn't require any device features (but it can make use of some)
+ Fast, built with Vulkan 1.1 and doesn't require any device features (but it can make use of some)
+ Simple and fully-featured cameras, allowing for multiple concurrent cameras
+ Powerful and very simple shader interface
+ Simple access to the Vulkan implementation through `VK2D/VulkanInterface.h`
Expand Down Expand Up @@ -54,8 +54,10 @@ Vulkan2D also requires the following external dependencies:

Example
=======
Using the renderer is quite simple, but there are some things to be aware of. For the sake
of brevity, error checking is removed from the following example

By default the program automatically crashes on fatal errors, but you may specify Vulkan2D to not do
that and check for errors on your own. The following example uses default settings meaning that if there
is an error in VK2D, it will print the status to `vk2derror.txt` and quit.

```c
SDL_Window *window = SDL_CreateWindow("VK2D", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_VULKAN);
Expand Down Expand Up @@ -90,6 +92,12 @@ vk2dRendererQuit();
SDL_DestroyWindow(window);
```
If you don't want VK2D to crash on errors you may specify that in the struct `VK2DStartupOptions` passed to
`vk2dRendererInit` and check for errors yourself with `vk2dStatus`, `vk2dStatusMessage`, and
`vk2dStatusFatal`. Any VK2D function can raise fatal errors but unless you pass bad pointers
to VK2D functions, they will not crash if there is a fatal error and will instead simply do
nothing.
Running the Examples
====================
All examples are tested to work on Windows and Ubuntu. The `CMakeLists.txt` at the root
Expand All @@ -99,13 +107,17 @@ shader before running the `examples/main/` example with:
glslc assets/test.frag -o assets/tex.frag.spv
glslc assets/test.vert -o assets/tex.vert.spv
You may also compile the binary shader blobs with the long command
If you don't trust binary blobs you may also compile the binary shader blobs with the command
genblobs.py colour.vert colour.frag instanced.vert instanced.frag model.vert model.frag shadows.vert shadows.frag tex.vert tex.frag
run from the `shaders/` folder (requires Python).
TODO
====
Roadmap
=======
+ Built-in imgui support
+ Better multi-threaded loading support
+ Soft shadows
+ More flexibility with shader uniforms
+ 3D shaders
74 changes: 34 additions & 40 deletions VK2D/Renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ static const int BASE_LAYER_COUNT = 0;
static const int BASE_EXTENSION_COUNT = 0;

static VK2DStartupOptions DEFAULT_STARTUP_OPTIONS = {
false,
true,
true,
"vk2derror.txt",
false,
256 * 1000
.enableDebug = false,
.stdoutLogging = true,
.quitOnError = true,
.errorFile = "vk2derror.txt",
.loadCustomShaders = false,
.vramPageSize = 256 * 1000
};

/******************************* User-visible functions *******************************/
Expand All @@ -63,20 +63,20 @@ VK2DResult vk2dRendererInit(SDL_Window *window, VK2DRendererConfig config, VK2DS
uint32_t totalExtensionCount, i, sdlExtensions;
const char** totalExtensions;

// Find the startup options
VK2DStartupOptions userOptions;
if (options == NULL) {
userOptions = DEFAULT_STARTUP_OPTIONS;
} else {
userOptions = *options;
if (userOptions.vramPageSize == 0)
userOptions.vramPageSize = 256 * 1024;
}

// Validation initialization needs to happen right away
vk2dValidationBegin(options->errorFile, options->quitOnError);
vk2dValidationBegin(userOptions.errorFile, userOptions.quitOnError);

if (vk2dRendererGetPointer() != NULL) {
// Find the startup options
VK2DStartupOptions userOptions;
if (options == NULL) {
userOptions = DEFAULT_STARTUP_OPTIONS;
} else {
userOptions = *options;
if (userOptions.vramPageSize == 0)
userOptions.vramPageSize = 256 * 1024;
}

// Print all available layers
VkLayerProperties *systemLayers;
uint32_t systemLayerCount;
Expand Down Expand Up @@ -345,7 +345,6 @@ void vk2dRendererStartFrame(const vec4 clearColour) {
} else {
vk2dRaise(VK2D_STATUS_VULKAN_ERROR, "Failed to acquire next image, Vulkan error %i.", result);
}
vk2dRendererQuit();
return;
}

Expand Down Expand Up @@ -376,14 +375,12 @@ void vk2dRendererStartFrame(const vec4 clearColour) {
VkResult result2 = vkResetCommandBuffer(gRenderer->dbCommandBuffer[gRenderer->scImageIndex], 0);
if (result != VK_SUCCESS || result2 != VK_SUCCESS) {
vk2dRaise(VK2D_STATUS_OUT_OF_VRAM, "Failed to reset command buffer at start of frame.");
vk2dRendererQuit();
return;
}
result = vkBeginCommandBuffer(gRenderer->commandBuffer[gRenderer->scImageIndex], &beginInfo);
result2 = vkBeginCommandBuffer(gRenderer->dbCommandBuffer[gRenderer->scImageIndex], &beginInfo);
if (result != VK_SUCCESS || result2 != VK_SUCCESS) {
vk2dRaise(VK2D_STATUS_VULKAN_ERROR, "Failed to begin command buffer at start of frame, Vulkan error %i/%i.", result, result2);
vk2dRendererQuit();
return;
}

Expand Down Expand Up @@ -430,7 +427,7 @@ void vk2dRendererStartFrame(const vec4 clearColour) {

VK2DResult vk2dRendererEndFrame() {
VK2DResult res = VK2D_SUCCESS;
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (gRenderer->procedStartFrame) {
gRenderer->procedStartFrame = false;

Expand All @@ -446,7 +443,6 @@ VK2DResult vk2dRendererEndFrame() {
VkResult result2 = vkEndCommandBuffer(gRenderer->dbCommandBuffer[gRenderer->scImageIndex]);
if (result != VK_SUCCESS || result2 != VK_SUCCESS) {
vk2dRaise(VK2D_STATUS_VULKAN_ERROR, "Failed to begin command buffer at start of frame, Vulkan error %i/%i.", result, result2);
vk2dRendererQuit();
return VK2D_ERROR;
}

Expand All @@ -465,7 +461,6 @@ VK2DResult vk2dRendererEndFrame() {
// Submit queue
if (vkResetFences(gRenderer->ld->dev, 1, &gRenderer->inFlightFences[gRenderer->currentFrame]) != VK_SUCCESS) {
vk2dRaise(VK2D_STATUS_OUT_OF_VRAM, "Failed to reset fences.");
vk2dRendererQuit();
return VK2D_ERROR;
}
result = vkQueueSubmit(gRenderer->ld->queue, 1, &submitInfo,
Expand All @@ -475,7 +470,6 @@ VK2DResult vk2dRendererEndFrame() {
vk2dRaise(VK2D_STATUS_DEVICE_LOST, "Vulkan device lost.");
else
vk2dRaise(VK2D_STATUS_VULKAN_ERROR, "Failed to submit queue, Vulkan error %i.", result);
vk2dRendererQuit();
return VK2D_ERROR;
}

Expand Down Expand Up @@ -518,7 +512,7 @@ VK2DLogicalDevice vk2dRendererGetDevice() {
}

void vk2dRendererSetTarget(VK2DTexture target) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (target != gRenderer->target) {
// In case the user attempts to switch targets from one texture to another
if (target != VK2D_TARGET_SCREEN && gRenderer->target != VK2D_TARGET_SCREEN) {
Expand Down Expand Up @@ -649,15 +643,15 @@ double vk2dRendererGetAverageFrameTime() {
}

void vk2dRendererClear() {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
VkDescriptorSet set = gRenderer->unitUBOSet;
_vk2dRendererDrawRaw(&set, 1, gRenderer->unitSquare, gRenderer->primFillPipe, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0,
0, VK2D_INVALID_CAMERA);
}
}

void vk2dRendererEmpty() {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
// Save old renderer state to revert back
VK2DBlendMode bm = vk2dRendererGetBlendMode();
vec4 c;
Expand All @@ -683,39 +677,39 @@ VK2DRendererLimits vk2dRendererGetLimits() {
}

void vk2dRendererDrawRectangle(float x, float y, float w, float h, float r, float ox, float oy) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
vk2dRendererDrawPolygon(gRenderer->unitSquare, x, y, true, 1, w, h, r, ox / w, oy / h);
}
}

void vk2dRendererDrawRectangleOutline(float x, float y, float w, float h, float r, float ox, float oy, float lineWidth) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
vk2dRendererDrawPolygon(gRenderer->unitSquareOutline, x, y, false, lineWidth, w, h, r, ox / w, oy / h);
}
}

void vk2dRendererDrawCircle(float x, float y, float r) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
vk2dRendererDrawPolygon(gRenderer->unitCircle, x, y, true, 1, r * 2, r * 2, 0, 0, 0);
}
}

void vk2dRendererDrawCircleOutline(float x, float y, float r, float lineWidth) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
vk2dRendererDrawPolygon(gRenderer->unitCircleOutline, x, y, false, lineWidth, r * 2, r * 2, 0, 0, 0);
}
}

void vk2dRendererDrawLine(float x1, float y1, float x2, float y2) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
float x = sqrtf(powf(y2 - y1, 2) + powf(x2 - x1, 2));
float r = atan2f(y2 - y1, x2 - x1);
vk2dRendererDrawPolygon(gRenderer->unitLine, x1, y1, false, 1, x, 1, r, 0, 0);
}
}

void vk2dRendererDrawShader(VK2DShader shader, void *data, VK2DTexture tex, float x, float y, float xscale, float yscale, float rot, float originX, float originY, float xInTex, float yInTex, float texWidth, float texHeight) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (shader != NULL) {
VkDescriptorSet sets[4];
sets[1] = gRenderer->samplerSet;
Expand Down Expand Up @@ -749,7 +743,7 @@ void vk2dRendererDrawShader(VK2DShader shader, void *data, VK2DTexture tex, floa
}

void vk2dRendererDrawInstanced(VK2DTexture tex, VK2DDrawInstance *instances, uint32_t count) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
VkDescriptorSet sets[3];
sets[1] = gRenderer->samplerSet;
sets[2] = tex->img->set;
Expand All @@ -760,7 +754,7 @@ void vk2dRendererDrawInstanced(VK2DTexture tex, VK2DDrawInstance *instances, uin
}

void vk2dRendererDrawTexture(VK2DTexture tex, float x, float y, float xscale, float yscale, float rot, float originX, float originY, float xInTex, float yInTex, float texWidth, float texHeight) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (tex != NULL) {
VkDescriptorSet sets[3];
sets[1] = gRenderer->samplerSet;
Expand All @@ -774,7 +768,7 @@ void vk2dRendererDrawTexture(VK2DTexture tex, float x, float y, float xscale, fl
}

void vk2dRendererDrawPolygon(VK2DPolygon polygon, float x, float y, bool filled, float lineWidth, float xscale, float yscale, float rot, float originX, float originY) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (polygon != NULL) {
VkDescriptorSet set;
_vk2dRendererDraw(&set, 1, polygon, filled ? gRenderer->primFillPipe : gRenderer->primLinePipe, x, y,
Expand All @@ -787,7 +781,7 @@ void vk2dRendererDrawPolygon(VK2DPolygon polygon, float x, float y, bool filled,
}

void vk2dRendererDrawGeometry(VK2DVertexColour *vertices, int count, float x, float y, bool filled, float lineWidth, float xscale, float yscale, float rot, float originX, float originY) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (vertices != NULL && count > 0) {
if (count <= gRenderer->limits.maxGeometryVertices) {
// Copy vertex data to the current descriptor buffer
Expand Down Expand Up @@ -815,7 +809,7 @@ void vk2dRendererDrawGeometry(VK2DVertexColour *vertices, int count, float x, fl
}

void vk2dRendererDrawShadows(VK2DShadowEnvironment shadowEnvironment, vec4 colour, vec2 lightSource) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (shadowEnvironment != NULL && shadowEnvironment->vbo != NULL) {
_vk2dRendererDrawShadows(shadowEnvironment, colour, lightSource);
_vk2dRendererResetBoundPointers();
Expand All @@ -826,7 +820,7 @@ void vk2dRendererDrawShadows(VK2DShadowEnvironment shadowEnvironment, vec4 colou
}

void vk2dRendererDrawModel(VK2DModel model, float x, float y, float z, float xscale, float yscale, float zscale, float rot, vec3 axis, float originX, float originY, float originZ) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (model != NULL) {
VkDescriptorSet sets[3];
sets[1] = gRenderer->modelSamplerSet;
Expand All @@ -840,7 +834,7 @@ void vk2dRendererDrawModel(VK2DModel model, float x, float y, float z, float xsc
}

void vk2dRendererDrawWireframe(VK2DModel model, float x, float y, float z, float xscale, float yscale, float zscale, float rot, vec3 axis, float originX, float originY, float originZ, float lineWidth) {
if (vk2dRendererGetPointer() != NULL) {
if (vk2dRendererGetPointer() != NULL && !vk2dStatusFatal()) {
if (model != NULL) {
VkDescriptorSet sets[3];
sets[1] = gRenderer->modelSamplerSet;
Expand Down
19 changes: 19 additions & 0 deletions VK2D/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ extern "C" {
/// VK2DStartupOptions lets you control how some meta things in the renderer, such as
/// whether or not to enable stdout logging or enable the Vulkan validation layers. Leave this
/// null for options that would generally be fine for most things.
///
/// The following are default values for VK2DStartupOptions if none are provided:
///
/// `enableDebug` defaults to `false`
/// `stdoutLogging` defaults to `true`
/// `quitOnError` defaults to `true`
/// `errorFile` defaults to `"vk2derror.txt"`
/// `loadCustomShaders` defaults to `false`
/// `vramPageSize` defaults to `256 * 1000`, setting this to 0 also uses `256 * 1000`
///
VK2DResult vk2dRendererInit(SDL_Window *window, VK2DRendererConfig config, VK2DStartupOptions *options);

/// \brief Waits until current GPU tasks are done before moving on
Expand Down Expand Up @@ -483,11 +493,20 @@ VK2DStatus vk2dStatus();

/// \brief Returns true if the current status code should be considered fatal
/// \returns Returns if the current renderer status is fatal.
///
/// If you have `VK2DStartupOptions.quitOnError` set to true (which is the default option)
/// a fatal status would have already crashed the program before you can check this. You
/// may, however, choose to disable crashing on an error and quit gracefully on your own in
/// which case this is very helpful. Some status are not considered fatal, like if a texture
/// file is missing so you should use this to check for critical errors and not vk2dStatus().
bool vk2dStatusFatal();

/// \brief Returns the current renderer status message, generally use this if vk2dGetStatus() returns something other than
/// VK2D_STATUS_NONE
/// \return Returns a message describing the most recent status code.
///
/// Usually the status code is descriptive enough to figure out what happened but this can be helpful for a user facing
/// error message. This is automatically put to a file if `VK2DStartupOptions.errorFile` is a valid filename.
const char *vk2dStatusMessage();

/// \brief Returns a string detailing information about the host machine
Expand Down
3 changes: 2 additions & 1 deletion examples/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "../debug.c"

/************************ Constants ************************/

const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;

Expand Down Expand Up @@ -89,7 +90,7 @@ int main(int argc, const char *argv[]) {
float prevMY = 0;
float shaderFloat = 0;

while (!quit) {
while (!quit && !vk2dStatusFatal()) {
delta = ((double)SDL_GetPerformanceCounter() - lastTime) / (double)SDL_GetPerformanceFrequency();
lastTime = SDL_GetPerformanceCounter();

Expand Down

0 comments on commit eb8009e

Please sign in to comment.