Shaders assets and loading fix
- a = { ... } definitions replaced with a { ... }
- GetOverlappedResult replaced with waiting for event in Windows async file loading
- FileHandles and AssetHandles stored in heap for container element adresses consistence
- Shader assets examples
This commit is contained in:
parent
9a258f7443
commit
7ad2ec4dc8
@ -11,12 +11,12 @@ struct WorldBenchmark : testing::Test
|
||||
{
|
||||
for (int16_t i = 0; i < 1000; i++)
|
||||
{
|
||||
ee_economy::Container resources = {.capacity = 100.0};
|
||||
ee_economy::Container resources {.capacity = 100.0};
|
||||
resources.add(ee_economy::RESOURCE_TYPE::COAL, 100.0);
|
||||
entt::entity state = world.registerState(i, resources);
|
||||
|
||||
ee_economy::EnterpriseDefaults miner = {
|
||||
.container = {.capacity = 100.0},
|
||||
ee_economy::EnterpriseDefaults miner {
|
||||
.container {.capacity = 100.0},
|
||||
.resourceTarget = ee_economy::RESOURCE_TYPE::GOLD,
|
||||
.baseEfficiency = 1.0
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ TEST(WorldTest, RegisterState)
|
||||
{
|
||||
ee_ecs::World world;
|
||||
|
||||
ee_economy::Container res = {.capacity = 100.0};
|
||||
ee_economy::Container res {.capacity = 100.0};
|
||||
res.add(ee_economy::RESOURCE_TYPE::COAL, 98.0);
|
||||
entt::entity state = world.registerState(10, res);
|
||||
|
||||
@ -37,8 +37,8 @@ TEST(WorldTest, SpawnMiner)
|
||||
world.getRegistry().emplace<ee_economy::State>(state, (int16_t)0);
|
||||
world.getRegistry().emplace<ee_economy::Container>(state, 100.0);
|
||||
|
||||
ee_economy::EnterpriseDefaults defaults = {
|
||||
.container = {.capacity = 100.0 },
|
||||
ee_economy::EnterpriseDefaults defaults {
|
||||
.container {.capacity = 100.0 },
|
||||
.resourceTarget = ee_economy::RESOURCE_TYPE::COAL,
|
||||
.baseEfficiency = 1.0,
|
||||
};
|
||||
@ -62,8 +62,8 @@ TEST(WorldTest, SpawnDeliverer)
|
||||
world.getRegistry().emplace<ee_economy::State>(state, (int16_t)0);
|
||||
world.getRegistry().emplace<ee_economy::Container>(state, 100.0);
|
||||
|
||||
ee_economy::EnterpriseDefaults defaults = {
|
||||
.container = {.capacity = 100.0 },
|
||||
ee_economy::EnterpriseDefaults defaults {
|
||||
.container {.capacity = 100.0 },
|
||||
.resourceTarget = ee_economy::RESOURCE_TYPE::COAL,
|
||||
.baseEfficiency = 1.0,
|
||||
};
|
||||
|
||||
BIN
assets/shaders/helloworld.frag.spv
Normal file
BIN
assets/shaders/helloworld.frag.spv
Normal file
Binary file not shown.
BIN
assets/shaders/helloworld.vert.spv
Normal file
BIN
assets/shaders/helloworld.vert.spv
Normal file
Binary file not shown.
@ -13,3 +13,4 @@ struct AssetBinary : AssetData
|
||||
AssetBinary(uint8_t* &source, size_t sourceSize);
|
||||
~AssetBinary();
|
||||
};
|
||||
typedef AssetBinary AssetShader;
|
||||
@ -27,6 +27,7 @@ public:
|
||||
BINARY,
|
||||
|
||||
SHADER,
|
||||
MATERIAL,
|
||||
IMAGE
|
||||
} type = BINARY;
|
||||
|
||||
@ -82,6 +83,8 @@ public:
|
||||
void Request(const AssetStreamCommand &command);
|
||||
void Request(const AssetStreamCommand::Type &type);
|
||||
|
||||
void LoadShaders();
|
||||
|
||||
void LoadAsync(AssetInfo::Type type, const std::filesystem::path &path);
|
||||
AssetHandle& Find(const std::filesystem::path &path);
|
||||
void Redirect(AssetHandle &handle, void *resource);
|
||||
@ -96,7 +99,7 @@ private:
|
||||
IFileSystem *diskSystem;
|
||||
|
||||
std::vector<AssetInfo> activeAssets;
|
||||
std::unordered_map<std::filesystem::path, AssetHandle> assetsMapping;
|
||||
std::unordered_map<std::filesystem::path, AssetHandle*> assetsMapping;
|
||||
std::vector<size_t> freeAssetSlots;
|
||||
std::vector<AssetLoadingEntry> loadingEntries;
|
||||
|
||||
@ -111,7 +114,7 @@ private:
|
||||
template <Asset T> T& AssetManager::Get(const AssetHandle &handle)
|
||||
{
|
||||
std::atomic<void*> result;
|
||||
AssetStreamCommand command = {
|
||||
AssetStreamCommand command {
|
||||
.type = AssetStreamCommand::GET,
|
||||
.target = handle,
|
||||
.out = &result
|
||||
|
||||
@ -46,11 +46,13 @@ public:
|
||||
virtual FileHandle& Open(const std::filesystem::path &path, const uint32_t flags) = 0;
|
||||
virtual void ReadAsync(FileHandle &file, const uint32_t bufferSize, const uint32_t flags) = 0;
|
||||
virtual bool PollReady(FileHandle &file) = 0;
|
||||
virtual void Close(const FileHandle &file) = 0;
|
||||
virtual void Close(FileHandle &file) = 0;
|
||||
|
||||
~IFileSystem();
|
||||
|
||||
protected:
|
||||
std::vector<FileHandle> activeFiles;
|
||||
std::vector<FileHandle> failedFiles;
|
||||
std::vector<FileHandle*> activeFiles;
|
||||
std::vector<FileHandle*> failedFiles;
|
||||
std::vector<size_t> freeFileSlots;
|
||||
};
|
||||
|
||||
@ -60,7 +62,7 @@ public:
|
||||
FileHandle& Open(const std::filesystem::path &path, const uint32_t flags) override;
|
||||
void ReadAsync(FileHandle &file, const uint32_t bufferSize, uint32_t flags) override;
|
||||
bool PollReady(FileHandle &file) override;
|
||||
void Close(const FileHandle &file) override;
|
||||
void Close(FileHandle &file) override;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "AssetManager.hpp"
|
||||
#include "allocator.hpp"
|
||||
#include "Logger.h"
|
||||
#include <stdexcept>
|
||||
|
||||
AssetInfo::AssetInfo(void *resource)
|
||||
{
|
||||
@ -81,7 +82,7 @@ void AssetManager::Request(const AssetStreamCommand::Type &type)
|
||||
|
||||
void AssetManager::LoadAsync(AssetInfo::Type type, const std::filesystem::path &path)
|
||||
{
|
||||
AssetStreamCommand command = {
|
||||
AssetStreamCommand command {
|
||||
.type = AssetStreamCommand::LOAD,
|
||||
.targetPath = path,
|
||||
.targetType = type
|
||||
@ -92,7 +93,7 @@ void AssetManager::LoadAsync(AssetInfo::Type type, const std::filesystem::path &
|
||||
AssetHandle& AssetManager::Find(const std::filesystem::path &path)
|
||||
{
|
||||
std::atomic<void*> result;
|
||||
AssetStreamCommand command = {
|
||||
AssetStreamCommand command {
|
||||
.type = AssetStreamCommand::FIND,
|
||||
.targetPath = path,
|
||||
.out = &result
|
||||
@ -104,7 +105,7 @@ AssetHandle& AssetManager::Find(const std::filesystem::path &path)
|
||||
|
||||
void AssetManager::Start()
|
||||
{
|
||||
if (isRunning) throw std::exception("Asset Streaming can be started only once");
|
||||
if (isRunning) throw std::logic_error("Asset Streaming can be started only once");
|
||||
|
||||
isRunning.store(true);
|
||||
streamingThread = std::jthread([this](std::stop_token stop) {
|
||||
@ -156,7 +157,7 @@ void AssetManager::Loop()
|
||||
//TODO: functions for each type
|
||||
switch (asset.type)
|
||||
{
|
||||
case AssetInfo::SHADER:
|
||||
case AssetInfo::MATERIAL:
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -166,6 +167,7 @@ void AssetManager::Loop()
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetInfo::SHADER:
|
||||
case AssetInfo::BINARY:
|
||||
default:
|
||||
{
|
||||
@ -176,8 +178,13 @@ void AssetManager::Loop()
|
||||
}
|
||||
}
|
||||
|
||||
asset.isReady.store(true);
|
||||
entry.fileOwner->Close(*entry.file);
|
||||
#ifndef NDEBUG
|
||||
auto path = entry.file->debugPath.c_str();
|
||||
LOG(INFO, "Asset loaded: [%s]", path);
|
||||
#endif
|
||||
entry.file = nullptr;
|
||||
asset.isReady.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,7 +199,7 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
|
||||
{
|
||||
case AssetStreamCommand::LOAD:
|
||||
{
|
||||
LOG(INFO, "LOAD command");
|
||||
LOG(INFO, "LOAD command: [%s]", command.targetPath.c_str());
|
||||
|
||||
/* TODO: process path to select FS */
|
||||
/* TODO: select between async load and sync job task */
|
||||
@ -202,7 +209,7 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
|
||||
|
||||
diskSystem->ReadAsync(file, static_cast<uint32_t>(file.size), FILE_READ_SEQUENTIAL);
|
||||
|
||||
AssetHandle handle;
|
||||
AssetHandle* handle = Allocate<AssetHandle>();
|
||||
AssetInfo asset(nullptr);
|
||||
asset.type = command.targetType;
|
||||
|
||||
@ -218,11 +225,11 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
|
||||
activeAssets.push_back(std::move(asset));
|
||||
assetPos = activeAssets.size() - 1;
|
||||
}
|
||||
handle.id = assetPos;
|
||||
handle->id = assetPos;
|
||||
assetsMapping[command.targetPath] = handle;
|
||||
|
||||
AssetLoadingEntry loadingEntry = {
|
||||
.handle = handle,
|
||||
AssetLoadingEntry loadingEntry {
|
||||
.handle = *handle,
|
||||
.file = &file,
|
||||
.fileOwner = diskSystem
|
||||
};
|
||||
@ -234,12 +241,12 @@ void AssetManager::ProcessCommand(AssetStreamCommand &command)
|
||||
|
||||
case AssetStreamCommand::FIND:
|
||||
{
|
||||
std::unordered_map<std::filesystem::path, AssetHandle>::iterator mapping = assetsMapping.find(command.targetPath);
|
||||
std::unordered_map<std::filesystem::path, AssetHandle*>::iterator mapping = assetsMapping.find(command.targetPath);
|
||||
|
||||
if (mapping == assetsMapping.end())
|
||||
break;
|
||||
|
||||
command.out->store(&mapping->second);
|
||||
command.out->store(mapping->second);
|
||||
|
||||
command.type = AssetStreamCommand::COMPLETED;
|
||||
break;
|
||||
@ -280,6 +287,20 @@ void AssetManager::Release()
|
||||
}
|
||||
activeAssets.clear();
|
||||
|
||||
for (auto &mapping : assetsMapping)
|
||||
Deallocate(mapping.second);
|
||||
assetsMapping.clear();
|
||||
|
||||
LOG(INFO, "Asset Streaming is released");
|
||||
isRunning.store(false);
|
||||
}
|
||||
|
||||
void AssetManager::LoadShaders()
|
||||
{
|
||||
std::filesystem::path shadersRoot = "assets/shaders";
|
||||
for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(shadersRoot))
|
||||
{
|
||||
if (entry.is_directory()) continue;
|
||||
LoadAsync(AssetInfo::SHADER, entry.path());
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,24 @@
|
||||
#include "FileSystem.hpp"
|
||||
#include "Logger.h"
|
||||
#include "allocator.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
IFileSystem::~IFileSystem()
|
||||
{
|
||||
for (FileHandle* handle : activeFiles)
|
||||
Deallocate(handle);
|
||||
for (FileHandle* handle : failedFiles)
|
||||
Deallocate(handle);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint32_t flags)
|
||||
{
|
||||
FileHandle result;
|
||||
FileHandle* result = Allocate<FileHandle>();
|
||||
|
||||
#ifndef NDEBUG
|
||||
result.debugPath = path;
|
||||
result->debugPath = path;
|
||||
#endif
|
||||
|
||||
DWORD createFileMode = 0;
|
||||
@ -33,59 +42,83 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint
|
||||
{
|
||||
LOG(ERROR, "Failed to open file %s. Error: %d", path.c_str(), GetLastError());
|
||||
failedFiles.push_back(result);
|
||||
return failedFiles[result.id];
|
||||
return *result;
|
||||
}
|
||||
|
||||
result.win32Handle = file;
|
||||
result.size = GetFileSize(file, NULL);
|
||||
if (result.size == INVALID_FILE_SIZE)
|
||||
result->win32Handle = file;
|
||||
result->size = GetFileSize(file, NULL);
|
||||
if (result->size == INVALID_FILE_SIZE)
|
||||
LOG(ERROR, "Failed to get size of file %s. Error: %d", path.c_str(), GetLastError());
|
||||
|
||||
if (freeFileSlots.size() > 0)
|
||||
{
|
||||
activeFiles[freeFileSlots.back()] = result;
|
||||
result.id = freeFileSlots.back();
|
||||
size_t pos = freeFileSlots.back();
|
||||
result->id = pos;
|
||||
activeFiles[pos] = result;
|
||||
freeFileSlots.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
result->id = activeFiles.size();
|
||||
activeFiles.push_back(result);
|
||||
result.id = activeFiles.size() - 1;
|
||||
}
|
||||
|
||||
return activeFiles[result.id];
|
||||
return *result;
|
||||
}
|
||||
|
||||
void NativeFileSystem::ReadAsync(FileHandle &file, const uint32_t bufferSize, const uint32_t flags)
|
||||
{
|
||||
file.overlapped = {};
|
||||
file.overlapped = {
|
||||
.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)
|
||||
};
|
||||
file.buffer = AllocateArray<uint8_t>(bufferSize);
|
||||
|
||||
ReadFile(
|
||||
BOOL isSync = ReadFile(
|
||||
file.win32Handle,
|
||||
file.buffer,
|
||||
bufferSize,
|
||||
NULL,
|
||||
&file.overlapped
|
||||
);
|
||||
|
||||
if (isSync || GetLastError() != ERROR_IO_PENDING)
|
||||
LOG(ERROR, "File reading is synchronious while asynchronious is awaited");
|
||||
}
|
||||
|
||||
bool NativeFileSystem::PollReady(FileHandle &file)
|
||||
{
|
||||
DWORD transferredSize = 0L;
|
||||
BOOL status = GetOverlappedResult(
|
||||
file.win32Handle,
|
||||
&file.overlapped,
|
||||
&transferredSize,
|
||||
FALSE
|
||||
);
|
||||
if (!status && GetLastError() != ERROR_IO_INCOMPLETE)
|
||||
{
|
||||
LOG(ERROR, "Error in file status. Code: %d", GetLastError());
|
||||
return false;
|
||||
}
|
||||
// DWORD transferredSize = 0L;
|
||||
// BOOL status = GetOverlappedResult(
|
||||
// file.win32Handle,
|
||||
// &file.overlapped,
|
||||
// &transferredSize,
|
||||
// FALSE
|
||||
// );
|
||||
|
||||
if (file.size == transferredSize)
|
||||
// DWORD error = GetLastError();
|
||||
// if (!status && error != ERROR_IO_INCOMPLETE)
|
||||
// {
|
||||
// LOG(CRITICAL, "Error in reading file status. Code: %d", error);
|
||||
// throw std::runtime_error("Error in reading file status");
|
||||
// }
|
||||
|
||||
// if (file.size == transferredSize)
|
||||
// {
|
||||
// file.data = file.buffer;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// /* TODO: reserve size for data, shift and move */
|
||||
// }
|
||||
// file.buffer = nullptr;
|
||||
|
||||
DWORD status = WaitForSingleObject(file.overlapped.hEvent, 0);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
{
|
||||
if (file.size == file.bufferSize)
|
||||
{
|
||||
file.data = file.buffer;
|
||||
}
|
||||
@ -93,18 +126,42 @@ bool NativeFileSystem::PollReady(FileHandle &file)
|
||||
{
|
||||
/* TODO: reserve size for data, shift and move */
|
||||
}
|
||||
file.buffer = nullptr;
|
||||
|
||||
return status;
|
||||
break;
|
||||
}
|
||||
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_TIMEOUT:
|
||||
return false;
|
||||
|
||||
default:
|
||||
{
|
||||
LOG(CRITICAL, "Failed to get file status. Error: %d", GetLastError());
|
||||
throw std::runtime_error("Failed to get file status");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeFileSystem::Close(const FileHandle &file)
|
||||
void NativeFileSystem::Close(FileHandle &file)
|
||||
{
|
||||
if (file.buffer != nullptr)
|
||||
{
|
||||
DeallocateArray(file.buffer);
|
||||
file.buffer = nullptr;
|
||||
}
|
||||
if (file.data != nullptr)
|
||||
{
|
||||
DeallocateArray(file.data);
|
||||
file.data = nullptr;
|
||||
}
|
||||
|
||||
if (file.win32Handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file.win32Handle);
|
||||
activeFiles[file.id].win32Handle = INVALID_HANDLE_VALUE;
|
||||
file.win32Handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
freeFileSlots.push_back(file.id);
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
#include "Logger.h"
|
||||
#include <allocator.hpp>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
Renderer::Renderer(SDL_Window *window) : isRunning(false)
|
||||
{
|
||||
@ -228,7 +228,7 @@ void Renderer::SetupDevice()
|
||||
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
|
||||
priority += GPU_DISCRETE_PRIORITY;
|
||||
|
||||
for (VkSurfaceFormatKHR format : surfaceInfoBuffer.availableFormats)
|
||||
for (const VkSurfaceFormatKHR &format : surfaceInfoBuffer.availableFormats)
|
||||
{
|
||||
surfaceInfoBuffer.format = format;
|
||||
if (format.format == VK_FORMAT_B8G8R8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
@ -239,7 +239,7 @@ void Renderer::SetupDevice()
|
||||
}
|
||||
|
||||
surfaceInfoBuffer.presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
for (VkPresentModeKHR presentMode : surfaceInfoBuffer.availablePresentModes)
|
||||
for (const VkPresentModeKHR &presentMode : surfaceInfoBuffer.availablePresentModes)
|
||||
{
|
||||
if (presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)
|
||||
{
|
||||
@ -265,7 +265,7 @@ void Renderer::SetupDevice()
|
||||
|
||||
// Define queues
|
||||
std::vector<VkDeviceQueueCreateInfo> queuesCreateConfigs;
|
||||
std::set<uint32_t> uniqueQueueFamilies = {
|
||||
std::set<uint32_t> uniqueQueueFamilies {
|
||||
queueFamilies.graphics,
|
||||
queueFamilies.present
|
||||
};
|
||||
@ -284,11 +284,11 @@ void Renderer::SetupDevice()
|
||||
}
|
||||
|
||||
// Create logical device
|
||||
std::vector<const char*> deviceExtensions = {
|
||||
std::vector<const char*> deviceExtensions {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME
|
||||
};
|
||||
|
||||
VkDeviceCreateInfo deviceInfo = {
|
||||
VkDeviceCreateInfo deviceInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.queueCreateInfoCount = static_cast<uint32_t>(queuesCreateConfigs.size()),
|
||||
@ -355,7 +355,7 @@ void Renderer::SetupSwapchain()
|
||||
|
||||
if (queueFamilies.graphics != queueFamilies.present)
|
||||
{
|
||||
uint32_t queueFamiliesShare[] = {queueFamilies.graphics, queueFamilies.present};
|
||||
uint32_t queueFamiliesShare[] {queueFamilies.graphics, queueFamilies.present};
|
||||
swapchainCreateConfig.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swapchainCreateConfig.queueFamilyIndexCount = 2;
|
||||
swapchainCreateConfig.pQueueFamilyIndices = queueFamiliesShare;
|
||||
@ -386,13 +386,13 @@ void Renderer::SetupSwapchain()
|
||||
.image = frames[i],
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = surfaceInfo.format.format,
|
||||
.components = {
|
||||
.components {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
},
|
||||
.subresourceRange = {
|
||||
.subresourceRange {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
@ -413,7 +413,7 @@ void Renderer::Start()
|
||||
if (isRunning)
|
||||
{
|
||||
LOG(CRITICAL, "Renderer can be started only once");
|
||||
throw std::exception("Renderer can be started only once");
|
||||
throw std::logic_error("Renderer can be started only once");
|
||||
}
|
||||
|
||||
isRunning.store(true);
|
||||
@ -493,7 +493,7 @@ void Renderer::Request(const RenderCommand::Type &type)
|
||||
|
||||
void Renderer::Release()
|
||||
{
|
||||
for (VkImageView frameView : frameViews)
|
||||
for (VkImageView &frameView : frameViews)
|
||||
{
|
||||
vkDestroyImageView(device, frameView, nullptr);
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@ int SDL_main(int argc, char **argv)
|
||||
// Setup assets
|
||||
AssetManager assetManager;
|
||||
assetManager.Start();
|
||||
assetManager.LoadShaders();
|
||||
|
||||
// Setup Renderer
|
||||
Renderer renderer(window);
|
||||
|
||||
8
shaders/helloworld/helloworld.frag
Normal file
8
shaders/helloworld/helloworld.frag
Normal file
@ -0,0 +1,8 @@
|
||||
#version 330
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
12
shaders/helloworld/helloworld.vert
Normal file
12
shaders/helloworld/helloworld.vert
Normal file
@ -0,0 +1,12 @@
|
||||
#version 330
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(-1.0, 1.0),
|
||||
vec2(0.0, -1.0),
|
||||
vec2(1.0, 1.0)
|
||||
);
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user