aasdasdasdasd

This commit is contained in:
Joshua Ashton 2022-08-13 20:41:19 +00:00
parent ac52336049
commit 0b84e8b027
15 changed files with 220 additions and 47 deletions

3
.gitmodules vendored
View File

@ -4,3 +4,6 @@
[submodule "thirdparty/miniz"] [submodule "thirdparty/miniz"]
path = subprojects/miniz path = subprojects/miniz
url = https://github.com/richgel999/miniz url = https://github.com/richgel999/miniz
[submodule "subprojects/libspng"]
path = subprojects/libspng
url = https://github.com/randy408/libspng

View File

@ -13,4 +13,15 @@ namespace orange
{ {
return HashString(s, count); return HashString(s, count);
} }
inline size_t& hashCombine(size_t& seed) { (void)seed; return seed; }
template <typename T, typename... Rest>
inline size_t& hashCombine(size_t& seed, const T& v, Rest... rest)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
hashCombine(seed, rest...);
return seed;
}
} }

View File

@ -6,6 +6,18 @@
namespace orange namespace orange
{ {
//#define USE_32BIT_INDICES
#ifdef USE_32BIT_INDICES
using IndexType = uint32_t;
static constexpr VkIndexType VulkanIndexType = VK_INDEX_TYPE_UINT32;
static constexpr size_t MaxMeshIndex = UINT32_MAX;
#else
using IndexType = uint16_t;
static constexpr VkIndexType VulkanIndexType = VK_INDEX_TYPE_UINT16;
static constexpr size_t MaxMeshIndex = UINT16_MAX;
#endif
struct MeshVertexData struct MeshVertexData
{ {
MeshVertexData(MeshVertexType type) MeshVertexData(MeshVertexType type)
@ -64,9 +76,9 @@ namespace orange
MeshData(MeshVertexType type) MeshData(MeshVertexType type)
: vertexData{ type } {} : vertexData{ type } {}
MeshVertexData vertexData; MeshVertexData vertexData;
Vector<uint16_t> indices; Vector<IndexType> indices;
AABB bounds; AABB bounds;
}; };
Result<MeshData> ParseOBJ(StringView buffer); Result<MeshData> ParseOBJ(StringView buffer);

View File

@ -14,6 +14,7 @@ namespace orange
VkBuffer buffer; VkBuffer buffer;
VkDeviceSize offset; VkDeviceSize offset;
VkDeviceSize size; VkDeviceSize size;
void* ptr;
}; };
struct GPUMemoryBuffer struct GPUMemoryBuffer
@ -33,7 +34,8 @@ namespace orange
Result<BufferSlice> AllocSlice(VkDeviceSize size, VkDeviceSize alignment = 1u) Result<BufferSlice> AllocSlice(VkDeviceSize size, VkDeviceSize alignment = 1u)
{ {
uint32_t offset = Align(m_offset, alignment); uint32_t offset = Align(m_offset, alignment);
BufferSlice slice{ m_buffer.buffer, offset, size }; BufferSlice slice{ m_buffer.buffer, offset, size,
m_buffer.ptr ? reinterpret_cast<uint8_t*>(m_buffer.ptr) + offset : nullptr };
if (offset + size > m_buffer.size) if (offset + size > m_buffer.size)
return Result<BufferSlice>::Error(); return Result<BufferSlice>::Error();
@ -80,7 +82,8 @@ namespace orange
VulkanResult<VoidResult> BeginCommandBuffer(VkCommandBuffer buffer); VulkanResult<VoidResult> BeginCommandBuffer(VkCommandBuffer buffer);
VulkanResult<VoidResult> EndCommandBuffer(VkCommandBuffer buffer); VulkanResult<VoidResult> EndCommandBuffer(VkCommandBuffer buffer);
VulkanResult<GPUMemoryBuffer> CreateBuffer(VkDeviceSize size); VulkanResult<GPUMemoryBuffer> CreateBuffer(VkDeviceSize size);
VulkanResult<VkImage> CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format); VulkanResult<VkImage> CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage);
VulkanResult<VkImageView> CreateImageView(VkImage image, VkFormat format, VkImageAspectFlagBits aspect);
VulkanResult<VkShaderModule> CreateShader(Span<const uint32_t> code); VulkanResult<VkShaderModule> CreateShader(Span<const uint32_t> code);

View File

@ -25,6 +25,8 @@ namespace orange
bool Update(Input::InputHandler& handler); bool Update(Input::InputHandler& handler);
void EnableRelativeMouse(bool relative); void EnableRelativeMouse(bool relative);
void SetTitle(const char* title);
protected: protected:
friend Result<Window>; friend Result<Window>;
Window(SDL_Window* window); Window(SDL_Window* window);

View File

@ -21,4 +21,15 @@ orange_include = include_directories(['include', 'subprojects'])
sdl2_dep = dependency('SDL2') sdl2_dep = dependency('SDL2')
vulkan_dep = dependency('vulkan') # get rid of me! vulkan_dep = dependency('vulkan') # get rid of me!
miniz_proj = subproject('miniz', default_options: [
'default_library=static',
])
miniz_dep = miniz_proj.get_variable('miniz_dep')
spng_proj = subproject('libspng', default_options: [
'default_library=static',
'use_miniz=True',
])
spng_dep = spng_proj.get_variable('spng_dep')
subdir('src') subdir('src')

View File

@ -18,9 +18,12 @@
#include <vs_Mesh.h> #include <vs_Mesh.h>
#include <vs_Fullscreen.h> #include <vs_Fullscreen.h>
#include <fs_DebugVertColor.h> #include <fs_DebugVertColor.h>
#include <fs_Mesh.h>
#include <fs_SkyGradient.h> #include <fs_SkyGradient.h>
#include <fs_Red.h> #include <fs_Red.h>
#include <spng.h>
using namespace orange; using namespace orange;
struct MiniPipelineInfo struct MiniPipelineInfo
@ -103,7 +106,7 @@ static VulkanResult<VkPipeline> CreateGraphicsPipeline(VkDevice device, const Mi
VkPipelineRasterizationStateCreateInfo rasterizationInfo = VkPipelineRasterizationStateCreateInfo rasterizationInfo =
{ {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.cullMode = VK_CULL_MODE_NONE, //VK_CULL_MODE_BACK_BIT, .cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.0f, .lineWidth = 1.0f,
}; };
@ -191,6 +194,7 @@ int main(int argc, char** argv)
if (!r_swapchain) if (!r_swapchain)
return 1; return 1;
//auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/gman.obj");
auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/chair.obj"); auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/chair.obj");
//auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/cube.obj"); //auto r_objData = fs::OpenFileIntoTextBuffer("/home/joshua/cube.obj");
if (!r_objData) if (!r_objData)
@ -198,11 +202,16 @@ int main(int argc, char** argv)
auto r_mesh = ParseOBJ(*r_objData); auto r_mesh = ParseOBJ(*r_objData);
auto r_buffer = r_renderContext->CreateBuffer(256 * 1024 * 1024);
if (!r_buffer)
return 1;
auto pooler = MemoryPool{ *r_buffer };
auto r_vsMesh = r_renderContext->CreateShader(vs_Mesh); auto r_vsMesh = r_renderContext->CreateShader(vs_Mesh);
if (!r_vsMesh) return 1; if (!r_vsMesh) return 1;
auto r_vsFullscreen = r_renderContext->CreateShader(vs_Fullscreen); auto r_vsFullscreen = r_renderContext->CreateShader(vs_Fullscreen);
if (!r_vsFullscreen) return 1; if (!r_vsFullscreen) return 1;
auto r_fsMesh = r_renderContext->CreateShader(fs_DebugVertColor); auto r_fsMesh = r_renderContext->CreateShader(fs_Mesh);
if (!r_fsMesh) return 1; if (!r_fsMesh) return 1;
auto r_fsRed = r_renderContext->CreateShader(fs_Red); auto r_fsRed = r_renderContext->CreateShader(fs_Red);
if (!r_fsRed) return 1; if (!r_fsRed) return 1;
@ -354,10 +363,58 @@ int main(int argc, char** argv)
.vertexAttributes = Span<const VkVertexInputAttributeDescription>(nullptr, 0), .vertexAttributes = Span<const VkVertexInputAttributeDescription>(nullptr, 0),
}); });
auto r_buffer = r_renderContext->CreateBuffer(256 * 1024 * 1024); struct ImageUpload
if (!r_buffer) {
BufferSlice from;
VkImage to;
VkExtent3D extent;
};
Vector<ImageUpload> imageUploads;
auto r_pngData = fs::OpenFileIntoBuffer("/home/joshua/chair_color.png");
if (!r_pngData)
return 1; return 1;
spng_ctx *ctx = spng_ctx_new(0);
spng_set_png_buffer(ctx, r_pngData->data, r_pngData->size);
size_t pngOutSize = 0zu;
spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &pngOutSize);
auto pngLinearSlice = *pooler.AllocSlice(pngOutSize, 16);
spng_decode_image(ctx, pngLinearSlice.ptr, pngOutSize, SPNG_FMT_RGBA8, 0);
struct spng_ihdr ihdr;
spng_get_ihdr(ctx, &ihdr);
spng_ctx_free(ctx);
auto r_texture = r_renderContext->CreateImage(pooler, ihdr.width, ihdr.height, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_USAGE_SAMPLED_BIT);
auto r_textureView = r_renderContext->CreateImageView(*r_texture, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
VkDescriptorImageInfo descriptorImageInfo =
{
.imageView = *r_textureView,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
VkWriteDescriptorSet writeImageDescriptorSet[] =
{
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSet,
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.pImageInfo = &descriptorImageInfo,
},
};
vkUpdateDescriptorSets(r_renderContext->Device(), Size(writeImageDescriptorSet), writeImageDescriptorSet, 0, nullptr);
imageUploads.PushBack(ImageUpload
{
.from = pngLinearSlice,
.to = *r_texture,
.extent = VkExtent3D{ ihdr.width, ihdr.height, 1u },
});
struct UniformData struct UniformData
{ {
mat4 projection; mat4 projection;
@ -377,25 +434,12 @@ int main(int argc, char** argv)
.view = camera.view(), .view = camera.view(),
}; };
auto pooler = MemoryPool{ *r_buffer };
auto meshData = r_mesh->vertexData.View(); auto meshData = r_mesh->vertexData.View();
auto vertexSlice = *pooler.AllocSlice(meshData.size, 16); auto vertexSlice = *pooler.AllocSlice(meshData.size, 16);
auto indexSlice = *pooler.AllocSlice(r_mesh->indices.Size() * sizeof(uint16_t), 16); auto indexSlice = *pooler.AllocSlice(r_mesh->indices.Size() * sizeof(IndexType), 16);
auto uniformSlice = *pooler.AllocSlice(sizeof(UniformData), 16); auto uniformSlice = *pooler.AllocSlice(sizeof(UniformData), 16);
auto r_depthImage = r_renderContext->CreateImage(pooler, 1280, 720, depthFormat); auto r_depthImage = r_renderContext->CreateImage(pooler, 1280, 720, depthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
auto depthImageView = *r_renderContext->CreateImageView(*r_depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
VkImageViewCreateInfo depthImageViewInfo =
{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = *r_depthImage,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = depthFormat,
.subresourceRange = FirstDepthMipSubresourceRange,
};
VkImageView depthImageView = VK_NULL_HANDLE;
if (vkCreateImageView(r_renderContext->Device(), &depthImageViewInfo, nullptr, &depthImageView) != VK_SUCCESS)
return 1;
VkDescriptorBufferInfo bufferInfo = VkDescriptorBufferInfo bufferInfo =
{ {
@ -430,9 +474,14 @@ int main(int argc, char** argv)
while (r_window->Update(handler)) while (r_window->Update(handler))
{ {
const auto t2 = Time::now(); const auto t2 = Time::now();
const float dt = Seconds(t2 - t1).count(); auto delta = t2 - t1;
const float dt = Seconds(delta).count();
t1 = t2; t1 = t2;
//char title[64];
//stbsp_snprintf(title, sizeof(title), "CubeTest - %gfps", 1.0f / dt);
//r_window->SetTitle(title);
{ {
auto mouseDelta = handler.getMouseMotionDelta(); auto mouseDelta = handler.getMouseMotionDelta();
auto scrollDelta = handler.getMouseScrollDelta(); auto scrollDelta = handler.getMouseScrollDelta();
@ -477,6 +526,24 @@ int main(int argc, char** argv)
VkCommandBuffer cmdBuf = r_swapchain->CommandBuffer(); VkCommandBuffer cmdBuf = r_swapchain->CommandBuffer();
r_renderContext->BeginCommandBuffer(cmdBuf); r_renderContext->BeginCommandBuffer(cmdBuf);
{ {
for (auto& upload : imageUploads)
{
VkBufferImageCopy region =
{
.bufferOffset = upload.from.offset,
.imageSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0u,
.baseArrayLayer = 0u,
.layerCount = 1u,
},
.imageExtent = upload.extent,
};
vkCmdCopyBufferToImage(cmdBuf, upload.from.buffer, upload.to, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1, &region);
}
imageUploads.Clear();
VkViewport viewport = VkViewport viewport =
{ {
.x = 0.0f, .x = 0.0f,
@ -498,7 +565,7 @@ int main(int argc, char** argv)
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
vkCmdBindVertexBuffers2(cmdBuf, 0, 1, &vertexSlice.buffer, &vertexSlice.offset, &vertexSlice.size, nullptr); vkCmdBindVertexBuffers2(cmdBuf, 0, 1, &vertexSlice.buffer, &vertexSlice.offset, &vertexSlice.size, nullptr);
vkCmdBindIndexBuffer(cmdBuf, r_buffer->buffer, indexSlice.offset, VK_INDEX_TYPE_UINT16); vkCmdBindIndexBuffer(cmdBuf, r_buffer->buffer, indexSlice.offset, VulkanIndexType);
const VkImageMemoryBarrier undefinedToColorBarrier = const VkImageMemoryBarrier undefinedToColorBarrier =
{ {

View File

@ -1,4 +1,4 @@
executable('CubeTest', 'CubeTest.cpp', orange_src, orange_shaders, executable('CubeTest', 'CubeTest.cpp', orange_src, orange_shaders,
dependencies : [ sdl2_dep, vulkan_dep ], dependencies : [ sdl2_dep, vulkan_dep, spng_dep, miniz_dep ],
include_directories : [ orange_include ] include_directories : [ orange_include ]
) )

View File

@ -3,9 +3,22 @@
#include <Orange/Graphics/Mesh.h> #include <Orange/Graphics/Mesh.h>
#include <Orange/Core/Parse.h> #include <Orange/Core/Parse.h>
#include <Orange/Core/Hash.h>
#include <unordered_map>
namespace orange namespace orange
{ {
struct StaticVertexHash
{
size_t operator() (const StaticVertex& key) const
{
size_t hash = 0;
return hashCombine(hash, key.pos.x, key.pos.y, key.pos.z, key.uv.x, key.uv.y, key.normal.x, key.normal.y, key.normal.z);
}
};
Result<MeshData> ParseOBJ(StringView buffer) Result<MeshData> ParseOBJ(StringView buffer)
{ {
MeshData data{ MeshVertexType::Static }; MeshData data{ MeshVertexType::Static };
@ -14,6 +27,8 @@ namespace orange
Vector<vec2> uvs; Vector<vec2> uvs;
Vector<vec3> normals; Vector<vec3> normals;
std::unordered_map<StaticVertex, IndexType, StaticVertexHash> indexTracker;
const char* obj = buffer.data; const char* obj = buffer.data;
const char* end = buffer.data + buffer.size; const char* end = buffer.data + buffer.size;
while (obj != end) while (obj != end)
@ -32,9 +47,9 @@ namespace orange
} }
if (element == "v") if (element == "v")
positions.EmplaceBack(vtx[0], vtx[1], vtx[2]); positions.EmplaceBack(vtx[0], -vtx[2], vtx[1]); // TODO remove hack and make this customizable
else if (element == "vt") else if (element == "vt")
uvs.EmplaceBack(vtx[0], vtx[1]); uvs.EmplaceBack(vtx[0], 1.0f - vtx[1]); // Fix the space from gl -> vk/dx
else if (element == "vn") else if (element == "vn")
normals.EmplaceBack(vtx[0], vtx[1], vtx[2]); normals.EmplaceBack(vtx[0], vtx[1], vtx[2]);
} }
@ -69,7 +84,9 @@ namespace orange
} }
} }
for (int i = 0; i < 3; i++) // Do backwards so we can have front faces
// be CCW.
for (int i = 2; i >= 0; i--)
{ {
StaticVertex vertex = StaticVertex vertex =
{ {
@ -80,15 +97,19 @@ namespace orange
auto& vertices = data.vertexData.GetStaticVertices(); auto& vertices = data.vertexData.GetStaticVertices();
size_t vertexIdx = vertices.FindIdx(vertex); auto iter = indexTracker.find(vertex);
if (vertexIdx == vertices.InvalidIdx) size_t vertexIdx;
if (iter == indexTracker.end())
{ {
data.bounds.Extend(vertex.pos); data.bounds.Extend(vertex.pos);
vertexIdx = vertices.PushBack(vertex); vertexIdx = vertices.PushBack(vertex);
indexTracker.emplace(vertex, vertexIdx);
} }
else
vertexIdx = iter->second;
Assert(vertexIdx < UINT16_MAX); Assert(vertexIdx < MaxMeshIndex);
data.indices.PushBack(uint16_t(vertexIdx)); data.indices.PushBack(IndexType(vertexIdx));
} }
} }
else if (!element.Empty()) else if (!element.Empty())

View File

@ -113,7 +113,7 @@ namespace orange
return VulkanResult<GPUMemoryBuffer>::Success(memory, buffer, size, data); return VulkanResult<GPUMemoryBuffer>::Success(memory, buffer, size, data);
} }
VulkanResult<VkImage> RenderContext::CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format) VulkanResult<VkImage> RenderContext::CreateImage(MemoryPool& pool, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage)
{ {
VkImageCreateInfo imageInfo = VkImageCreateInfo imageInfo =
{ {
@ -125,7 +125,7 @@ namespace orange
.arrayLayers = 1u, .arrayLayers = 1u,
.samples = VK_SAMPLE_COUNT_1_BIT, .samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL, .tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, .usage = usage,
}; };
VkImage image = VK_NULL_HANDLE; VkImage image = VK_NULL_HANDLE;
@ -142,4 +142,30 @@ namespace orange
return VulkanResult<VkImage>::Success(image); return VulkanResult<VkImage>::Success(image);
} }
VulkanResult<VkImageView> RenderContext::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlagBits aspect)
{
VkImageViewCreateInfo imageViewInfo =
{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.subresourceRange =
{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
VkImageView imageView = VK_NULL_HANDLE;
VkResult res = VK_SUCCESS;
if ((res = vkCreateImageView(m_device, &imageViewInfo, nullptr, &imageView)) != VK_SUCCESS)
return VulkanResult<VkImageView>::Error(res);
return VulkanResult<VkImageView>::Success(imageView);
}
} }

View File

@ -0,0 +1,15 @@
#version 450
#extension GL_EXT_nonuniform_qualifier : enable
layout(set = 0, binding = 1) uniform sampler s_sampler;
layout(set = 0, binding = 2) uniform texture2D s_images[];
layout(location = 0) in flat int id;
layout(location = 1) in vec2 uv;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = texture(sampler2D(s_images[nonuniformEXT(id)], s_sampler), uv);
}

View File

@ -1,4 +1,5 @@
#version 450 #version 450
#extension GL_ARB_shader_draw_parameters : enable
layout(binding = 0) uniform UniformData { layout(binding = 0) uniform UniformData {
mat4 projection; mat4 projection;
@ -9,18 +10,12 @@ layout(location = 0) in vec3 i_pos;
layout(location = 1) in vec2 i_uv; layout(location = 1) in vec2 i_uv;
layout(location = 2) in vec3 i_normal; layout(location = 2) in vec3 i_normal;
layout(location = 0) out vec4 o_color; layout(location = 0) out flat int o_id;
layout(location = 1) out vec2 o_uv;
vec3 k_colors[3] = vec3[]
(
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
void main() void main()
{ {
gl_Position = u_data.projection * u_data.view * vec4(i_pos, 1.0); gl_Position = u_data.projection * u_data.view * vec4(i_pos, 1.0);
//o_color = vec4(k_colors[gl_VertexIndex % 3], 1.0); o_id = gl_DrawIDARB;
o_color = vec4(i_uv, 0.0f, 1.0); o_uv = i_uv;
} }

View File

@ -117,4 +117,9 @@ namespace orange
SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE); SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE);
SDL_ShowCursor(m_hasFocus && relative ? SDL_FALSE : SDL_TRUE); SDL_ShowCursor(m_hasFocus && relative ? SDL_FALSE : SDL_TRUE);
} }
void Window::SetTitle(const char* title)
{
SDL_SetWindowTitle(m_window, title);
}
} }

View File

@ -1,5 +1,6 @@
orange_shaders = glsl_generator.process([ orange_shaders = glsl_generator.process([
'Render/Shaders/fs_DebugVertColor.frag', 'Render/Shaders/fs_DebugVertColor.frag',
'Render/Shaders/fs_Mesh.frag',
'Render/Shaders/fs_Red.frag', 'Render/Shaders/fs_Red.frag',
'Render/Shaders/fs_SkyGradient.frag', 'Render/Shaders/fs_SkyGradient.frag',
'Render/Shaders/vs_Fullscreen.vert', 'Render/Shaders/vs_Fullscreen.vert',

1
subprojects/libspng Submodule

@ -0,0 +1 @@
Subproject commit c4126ad8ce0513ec16aa2e4180eb6545548710a0