presentaion queue handles + texture handle

This commit is contained in:
Greg Wells
2025-06-03 14:38:51 -04:00
parent fbc30509c4
commit c48e11f998
14 changed files with 69 additions and 59 deletions

View File

@@ -56,7 +56,7 @@ gnReturnCode gnCreateFramebufferFn(struct gnFramebuffer_t* framebuffer, struct g
if(!wasDepthStencil) { if(!wasDepthStencil) {
MTLRenderPassColorAttachmentDescriptor* color = framebuffer->framebuffer->framebuffer.colorAttachments[colorAttachment]; MTLRenderPassColorAttachmentDescriptor* color = framebuffer->framebuffer->framebuffer.colorAttachments[colorAttachment];
color.texture = info.attachments[i].texture->texture; color.texture = info.attachments[i]->texture->texture;
color.loadAction = mtlGryphnLoadOperation(info.renderPassDescriptor->info.attachmentInfos[i].loadOperation); color.loadAction = mtlGryphnLoadOperation(info.renderPassDescriptor->info.attachmentInfos[i].loadOperation);
color.storeAction = mtlGryphnStoreOperation(info.renderPassDescriptor->info.attachmentInfos[i].storeOperation); color.storeAction = mtlGryphnStoreOperation(info.renderPassDescriptor->info.attachmentInfos[i].storeOperation);

View File

@@ -13,8 +13,9 @@ gnReturnCode gnPresentFn(struct gnOutputDevice_t* device, struct gnPresentInfo_t
while (!info.waitSemaphores[i].semaphore->eventTriggered) {} while (!info.waitSemaphores[i].semaphore->eventTriggered) {}
} }
info.presentationQueues->info.surface->windowSurface->layer.device = device->outputDevice->device; for (int i =0 ; i < info.presentationQueueCount; i++) {
id<CAMetalDrawable> drawable = [info.presentationQueues->info.surface->windowSurface->layer nextDrawable]; info.presentationQueues[i]->info.surface->windowSurface->layer.device = device->outputDevice->device;
id<CAMetalDrawable> drawable = [info.presentationQueues[i]->info.surface->windowSurface->layer nextDrawable];
if (drawable == nil) { if (drawable == nil) {
return GN_FAILED_TO_CREATE_FRAMEBUFFER; return GN_FAILED_TO_CREATE_FRAMEBUFFER;
} }
@@ -29,30 +30,30 @@ gnReturnCode gnPresentFn(struct gnOutputDevice_t* device, struct gnPresentInfo_t
id<MTLRenderCommandEncoder> render = [commandBuffer renderCommandEncoderWithDescriptor:passDesc]; id<MTLRenderCommandEncoder> render = [commandBuffer renderCommandEncoderWithDescriptor:passDesc];
[render endEncoding]; [render endEncoding];
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder]; id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
for (int i =0 ; i < info.presentationQueueCount; i++) { [blit copyFromTexture:info.presentationQueues[i]->images[info.imageIndices[i]]->texture->texture
[blit copyFromTexture:info.presentationQueues[i].images[info.imageIndices[i]].texture->texture
sourceSlice:0 sourceSlice:0
sourceLevel:0 sourceLevel:0
sourceOrigin:(MTLOrigin){0, 0, 0} sourceOrigin:(MTLOrigin){0, 0, 0}
sourceSize:(MTLSize){info.presentationQueues[i].info.imageSize.x, info.presentationQueues[i].info.imageSize.y, 1} sourceSize:(MTLSize){info.presentationQueues[i]->info.imageSize.x, info.presentationQueues[i]->info.imageSize.y, 1}
toTexture:drawable.texture toTexture:drawable.texture
destinationSlice:0 destinationSlice:0
destinationLevel:0 destinationLevel:0
destinationOrigin:(MTLOrigin){0, 0, 0}]; destinationOrigin:(MTLOrigin){0, 0, 0}];
[blit endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
device->outputDevice->executingCommandBuffer = commandBuffer;
} }
[blit endEncoding]; [device->outputDevice->executingCommandBuffer waitUntilScheduled];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
[commandBuffer waitUntilScheduled];
device->outputDevice->executingCommandBuffer = commandBuffer;
for (int i = 0; i < info.presentationQueueCount; i++) { for (int i = 0; i < info.presentationQueueCount; i++) {
if (info.presentationQueues[i].info.imageSize.x != info.presentationQueues[i].info.surface->windowSurface->layer.drawableSize.width || if (info.presentationQueues[i]->info.imageSize.x != info.presentationQueues[i]->info.surface->windowSurface->layer.drawableSize.width ||
info.presentationQueues[i].info.imageSize.y != info.presentationQueues[i].info.surface->windowSurface->layer.drawableSize.height) { info.presentationQueues[i]->info.imageSize.y != info.presentationQueues[i]->info.surface->windowSurface->layer.drawableSize.height) {
return GN_SUBOPTIMAL_PRESENTATION_QUEUE; return GN_SUBOPTIMAL_PRESENTATION_QUEUE;
} }
} }

View File

@@ -5,7 +5,7 @@
#include "core/texture/metal_texture.h" #include "core/texture/metal_texture.h"
#include "core/sync/semaphore/metal_semaphore.h" #include "core/sync/semaphore/metal_semaphore.h"
gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueue* presentationQueue, const gnOutputDeviceHandle device, struct gnPresentationQueueInfo_t presentationInfo) { gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueueHandle presentationQueue, const gnOutputDeviceHandle device, struct gnPresentationQueueInfo_t presentationInfo) {
if (presentationInfo.minImageCount > 3) { if (presentationInfo.minImageCount > 3) {
gnDebuggerSetErrorMessage(device->instance->debugger, (gnMessageData){ gnDebuggerSetErrorMessage(device->instance->debugger, (gnMessageData){
.message = gnCreateString("On Metal you cannot have more than 3 images in a presentation queue") .message = gnCreateString("On Metal you cannot have more than 3 images in a presentation queue")
@@ -39,14 +39,15 @@ gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueue* presentationQueue,
presentationQueue->images = malloc(sizeof(gnTexture) * presentationInfo.minImageCount); presentationQueue->images = malloc(sizeof(gnTexture) * presentationInfo.minImageCount);
for (int i = 0; i < presentationInfo.minImageCount; i++) { for (int i = 0; i < presentationInfo.minImageCount; i++) {
presentationQueue->presentationQueue->textures[i] = [device->outputDevice->device newTextureWithDescriptor:textureDescriptor]; presentationQueue->presentationQueue->textures[i] = [device->outputDevice->device newTextureWithDescriptor:textureDescriptor];
presentationQueue->images[i].texture = malloc(sizeof(gnPlatformTexture)); presentationQueue->images[i] = malloc(sizeof(struct gnTexture_t));
presentationQueue->images[i].texture->texture = presentationQueue->presentationQueue->textures[i]; presentationQueue->images[i]->texture = malloc(sizeof(gnPlatformTexture));
presentationQueue->images[i]->texture->texture = presentationQueue->presentationQueue->textures[i];
} }
return GN_SUCCESS; return GN_SUCCESS;
} }
gnReturnCode gnPresentationQueueGetImageFn(gnPresentationQueue* presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex) { gnReturnCode gnPresentationQueueGetImageFn(gnPresentationQueueHandle presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex) {
semaphore->semaphore->eventTriggered = gnFalse; semaphore->semaphore->eventTriggered = gnFalse;
*imageIndex = presentationQueue->presentationQueue->currentImage; *imageIndex = presentationQueue->presentationQueue->currentImage;
presentationQueue->presentationQueue->currentImage++; presentationQueue->presentationQueue->currentImage++;
@@ -55,7 +56,7 @@ gnReturnCode gnPresentationQueueGetImageFn(gnPresentationQueue* presentationQueu
return GN_SUCCESS; return GN_SUCCESS;
} }
void gnDestroyPresentationQueueFn(gnPresentationQueue *presentationQueue) { void gnDestroyPresentationQueueFn(gnPresentationQueueHandle presentationQueue) {
for (int i = 0; i < presentationQueue->imageCount; i++) { for (int i = 0; i < presentationQueue->imageCount; i++) {
[presentationQueue->presentationQueue->textures[i] release]; [presentationQueue->presentationQueue->textures[i] release];
} }

View File

@@ -8,7 +8,7 @@ gnReturnCode gnCreateFramebufferFn(struct gnFramebuffer_t* framebuffer, struct g
VkImageView* attachments = malloc(sizeof(VkImageView) * info.attachmentCount); VkImageView* attachments = malloc(sizeof(VkImageView) * info.attachmentCount);
for (int i = 0; i < info.attachmentCount; i++) for (int i = 0; i < info.attachmentCount; i++)
attachments[i] = info.attachments[i].texture->imageView; attachments[i] = info.attachments[i]->texture->imageView;
VkFramebufferCreateInfo framebufferInfo = { VkFramebufferCreateInfo framebufferInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,

View File

@@ -10,7 +10,7 @@ gnReturnCode gnPresentFn(struct gnOutputDevice_t* device, struct gnPresentInfo_t
for (int i = 0; i < info.waitCount; i++) waitSemaphores[i] = info.waitSemaphores[i].semaphore->semaphore; for (int i = 0; i < info.waitCount; i++) waitSemaphores[i] = info.waitSemaphores[i].semaphore->semaphore;
VkSwapchainKHR* swapchains = malloc(sizeof(VkSwapchainKHR) * info.presentationQueueCount); VkSwapchainKHR* swapchains = malloc(sizeof(VkSwapchainKHR) * info.presentationQueueCount);
for (int i = 0; i < info.presentationQueueCount; i++) swapchains[i] = info.presentationQueues[i].presentationQueue->swapChain; for (int i = 0; i < info.presentationQueueCount; i++) swapchains[i] = info.presentationQueues[i]->presentationQueue->swapChain;
VkPresentInfoKHR presentInfo = { VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,

View File

@@ -5,8 +5,9 @@
#include "core/debugger/gryphn_debugger.h" #include "core/debugger/gryphn_debugger.h"
#include "textures/vulkan_texture.h" #include "textures/vulkan_texture.h"
#include "sync/semaphore/vulkan_semaphore.h" #include "sync/semaphore/vulkan_semaphore.h"
#include "stdio.h"
gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueue* presentationQueue, const gnOutputDeviceHandle device, struct gnPresentationQueueInfo_t presentationInfo) { gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueueHandle presentationQueue, const gnOutputDeviceHandle device, struct gnPresentationQueueInfo_t presentationInfo) {
presentationQueue->presentationQueue = malloc(sizeof(struct gnPlatformPresentationQueue_t)); presentationQueue->presentationQueue = malloc(sizeof(struct gnPlatformPresentationQueue_t));
vkSwapchainSupportDetails details = vkGetSwapchainSupport(device->physicalDevice.physicalDevice->device, presentationInfo.surface->windowSurface->surface); vkSwapchainSupportDetails details = vkGetSwapchainSupport(device->physicalDevice.physicalDevice->device, presentationInfo.surface->windowSurface->surface);
@@ -95,22 +96,23 @@ gnReturnCode gnCreatePresentationQueueFn(gnPresentationQueue* presentationQueue,
imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
imageViewCreateInfo.subresourceRange.layerCount = 1; imageViewCreateInfo.subresourceRange.layerCount = 1;
presentationQueue->images = malloc(sizeof(gnTexture) * presentationQueue->imageCount); presentationQueue->images = malloc(sizeof(gnTextureHandle) * presentationQueue->imageCount);
for (int i = 0; i < presentationQueue->imageCount; i++) { for (int i = 0; i < presentationQueue->imageCount; i++) {
presentationQueue->images[i].texture = malloc(sizeof(gnPlatformTexture)); presentationQueue->images[i] = malloc(sizeof(struct gnTexture_t));
presentationQueue->images[i]->texture = malloc(sizeof(gnPlatformTexture));
imageViewCreateInfo.image = presentationQueue->presentationQueue->swapChainImages[i]; imageViewCreateInfo.image = presentationQueue->presentationQueue->swapChainImages[i];
if (vkCreateImageView(device->outputDevice->device, &imageViewCreateInfo, NULL, &presentationQueue->presentationQueue->swapChainImageViews[i]) != VK_SUCCESS) { if (vkCreateImageView(device->outputDevice->device, &imageViewCreateInfo, NULL, &presentationQueue->presentationQueue->swapChainImageViews[i]) != VK_SUCCESS) {
return GN_FAILED_TO_CREATE_IMAGE_VIEW; return GN_FAILED_TO_CREATE_IMAGE_VIEW;
} }
presentationQueue->images[i].texture->image = presentationQueue->presentationQueue->swapChainImages[i]; presentationQueue->images[i]->texture->image = presentationQueue->presentationQueue->swapChainImages[i];
presentationQueue->images[i].texture->imageView = presentationQueue->presentationQueue->swapChainImageViews[i]; presentationQueue->images[i]->texture->imageView = presentationQueue->presentationQueue->swapChainImageViews[i];
} }
return GN_SUCCESS; return GN_SUCCESS;
} }
gnReturnCode gnPresentationQueueGetImageFn(gnPresentationQueue* presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex) { gnReturnCode gnPresentationQueueGetImageFn(gnPresentationQueueHandle presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex) {
VkResult result = vkAcquireNextImageKHR( VkResult result = vkAcquireNextImageKHR(
presentationQueue->outputDevice->outputDevice->device, presentationQueue->outputDevice->outputDevice->device,
presentationQueue->presentationQueue->swapChain, presentationQueue->presentationQueue->swapChain,
@@ -122,7 +124,7 @@ gnReturnCode gnPresentationQueueGetImageFn(gnPresentationQueue* presentationQueu
return GN_SUCCESS; return GN_SUCCESS;
} }
void gnDestroyPresentationQueueFn(gnPresentationQueue* queue) { void gnDestroyPresentationQueueFn(gnPresentationQueueHandle queue) {
for (int i = 0; i < queue->imageCount; i++) for (int i = 0; i < queue->imageCount; i++)
vkDestroyImageView(queue->outputDevice->outputDevice->device, queue->presentationQueue->swapChainImageViews[i], NULL); vkDestroyImageView(queue->outputDevice->outputDevice->device, queue->presentationQueue->swapChainImageViews[i], NULL);
vkDestroySwapchainKHR(queue->outputDevice->outputDevice->device, queue->presentationQueue->swapChain, NULL); vkDestroySwapchainKHR(queue->outputDevice->outputDevice->device, queue->presentationQueue->swapChain, NULL);

View File

@@ -1,12 +1,11 @@
#pragma once #pragma once
#include "core/renderpass/gryphn_render_pass_descriptor.h" #include "core/renderpass/gryphn_render_pass_descriptor.h"
#include "core/textures/gryphn_texture.h"
#include "utils/math/gryphn_vec2.h" #include "utils/math/gryphn_vec2.h"
typedef struct gnFramebufferInfo_t { typedef struct gnFramebufferInfo_t {
struct gnRenderPassDescriptor_t* renderPassDescriptor; struct gnRenderPassDescriptor_t* renderPassDescriptor;
uint32_t attachmentCount; uint32_t attachmentCount;
struct gnTexture_t* attachments; gnTextureHandle* attachments;
gnUInt2 size; gnUInt2 size;
} gnFramebufferInfo; } gnFramebufferInfo;

View File

@@ -7,3 +7,5 @@ typedef struct type##_t* type
GN_HANDLE(gnInstance); GN_HANDLE(gnInstance);
GN_HANDLE(gnDebugger); GN_HANDLE(gnDebugger);
GN_HANDLE(gnWindowSurface); GN_HANDLE(gnWindowSurface);
GN_HANDLE(gnPresentationQueue);
GN_HANDLE(gnTexture);

View File

@@ -59,9 +59,9 @@ typedef struct gnFunctions_t {
#include "core/presentation_queue/gryphn_presentation_queue.h" #include "core/presentation_queue/gryphn_presentation_queue.h"
typedef struct gnDeviceFunctions_t { typedef struct gnDeviceFunctions_t {
gnReturnCode (*_gnCreatePresentationQueue)(gnPresentationQueue* presentationQueue, const gnOutputDeviceHandle device, struct gnPresentationQueueInfo_t presentationInfo); gnReturnCode (*_gnCreatePresentationQueue)(gnPresentationQueueHandle presentationQueue, const gnOutputDeviceHandle device, struct gnPresentationQueueInfo_t presentationInfo);
gnReturnCode (*_gnPresentationQueueGetImage)(gnPresentationQueue* presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex); gnReturnCode (*_gnPresentationQueueGetImage)(gnPresentationQueueHandle presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex);
void (*_gnDestroyPresentationQueue)(gnPresentationQueue *presentationQueue); void (*_gnDestroyPresentationQueue)(gnPresentationQueueHandle presentationQueue);
gnReturnCode (*_gnCreateShaderModule)(struct gnShaderModule_t* module, gnOutputDeviceHandle device, struct gnShaderModuleInfo_t shaderModuleInfo); gnReturnCode (*_gnCreateShaderModule)(struct gnShaderModule_t* module, gnOutputDeviceHandle device, struct gnShaderModuleInfo_t shaderModuleInfo);
void (*_gnDestroyShaderModule)(struct gnShaderModule_t* module); void (*_gnDestroyShaderModule)(struct gnShaderModule_t* module);

View File

@@ -6,7 +6,7 @@ typedef struct gnPresentInfo_t {
uint32_t waitCount; uint32_t waitCount;
struct gnSemaphore_t* waitSemaphores; struct gnSemaphore_t* waitSemaphores;
uint32_t presentationQueueCount; uint32_t presentationQueueCount;
struct gnPresentationQueue_t* presentationQueues; gnPresentationQueueHandle* presentationQueues;
uint32_t* imageIndices; uint32_t* imageIndices;
uint32_t queueIndex; uint32_t queueIndex;
} gnPresentInfo; } gnPresentInfo;

View File

@@ -1,5 +0,0 @@
#pragma once
typedef enum gnPresentationQueueState_e {
GN_OUT_OF_DATE, GN_SUBOPTIMAL, GN_VALID
} gnPresentationQueueState;

View File

@@ -1,16 +1,21 @@
#include "gryphn_presentation_queue.h" #include "gryphn_presentation_queue.h"
#include "core/gryphn_platform_functions.h" #include "core/gryphn_platform_functions.h"
gnReturnCode gnCreatePresentationQueue(gnPresentationQueue* presentationQueue, struct gnOutputDevice_t* device, struct gnPresentationQueueInfo_t presentationInfo){ gnReturnCode gnCreatePresentationQueue(gnPresentationQueueHandle* presentationQueue, struct gnOutputDevice_t* device, struct gnPresentationQueueInfo_t presentationInfo){
presentationQueue->outputDevice = device; *presentationQueue = malloc(sizeof(struct gnPresentationQueue_t));
presentationQueue->info = presentationInfo; (*presentationQueue)->outputDevice = device;
return device->deviceFunctions->_gnCreatePresentationQueue(presentationQueue, device, presentationInfo); (*presentationQueue)->info = presentationInfo;
return device->deviceFunctions->_gnCreatePresentationQueue(*presentationQueue, device, presentationInfo);
} }
gnReturnCode gnPresentationQueueGetImage(gnPresentationQueue* presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex) { gnReturnCode gnPresentationQueueGetImage(gnPresentationQueueHandle presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex) {
return presentationQueue->outputDevice->deviceFunctions->_gnPresentationQueueGetImage(presentationQueue, timeout, semaphore, imageIndex); return presentationQueue->outputDevice->deviceFunctions->_gnPresentationQueueGetImage(presentationQueue, timeout, semaphore, imageIndex);
} }
uint32_t gnGetPresentationQueueImageCount(gnPresentationQueueHandle presentationQueue) { return presentationQueue->imageCount; }
void gnDestroyPresentationQueue(gnPresentationQueue *presentationQueue) { gnTextureHandle gnGetPresentationQueueImage(gnPresentationQueueHandle presentationQueue, uint32_t index) {
presentationQueue->outputDevice->deviceFunctions->_gnDestroyPresentationQueue(presentationQueue); return presentationQueue->images[index];
}
void gnDestroyPresentationQueue(gnPresentationQueueHandle presentationQueue) {
presentationQueue->outputDevice->deviceFunctions->_gnDestroyPresentationQueue(presentationQueue);
free(presentationQueue);
} }

View File

@@ -4,6 +4,7 @@
#include <core/window_surface/gryphn_surface.h> #include <core/window_surface/gryphn_surface.h>
#include <utils/types/gryphn_image_format.h> #include <utils/types/gryphn_image_format.h>
#include <core/sync/semaphore/gryphn_semaphore.h> #include <core/sync/semaphore/gryphn_semaphore.h>
#include "core/gryphn_handles.h"
typedef struct gnPresentationQueueInfo_t { typedef struct gnPresentationQueueInfo_t {
uint32_t minImageCount; uint32_t minImageCount;
@@ -17,15 +18,19 @@ typedef struct gnPresentationQueueInfo_t {
struct gnPlatformPresentationQueue_t; struct gnPlatformPresentationQueue_t;
typedef struct gnPresentationQueue_t { #ifdef GN_REVEAL_IMPL
struct gnPresentationQueue_t {
struct gnPlatformPresentationQueue_t* presentationQueue; struct gnPlatformPresentationQueue_t* presentationQueue;
struct gnOutputDevice_t* outputDevice; gnOutputDeviceHandle outputDevice;
gnBool valid; gnBool valid;
uint32_t imageCount; uint32_t imageCount;
struct gnTexture_t* images; gnTextureHandle* images;
struct gnPresentationQueueInfo_t info; struct gnPresentationQueueInfo_t info;
} gnPresentationQueue; };
#endif
gnReturnCode gnCreatePresentationQueue(gnPresentationQueue* presentationQueue, struct gnOutputDevice_t* device, struct gnPresentationQueueInfo_t presentationInfo); gnReturnCode gnCreatePresentationQueue(gnPresentationQueueHandle* presentationQueue, struct gnOutputDevice_t* device, struct gnPresentationQueueInfo_t presentationInfo);
gnReturnCode gnPresentationQueueGetImage(gnPresentationQueue* presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex); gnReturnCode gnPresentationQueueGetImage(gnPresentationQueueHandle presentationQueue, uint64_t timeout, struct gnSemaphore_t* semaphore, uint32_t* imageIndex);
void gnDestroyPresentationQueue(gnPresentationQueue* presentationQueue); uint32_t gnGetPresentationQueueImageCount(gnPresentationQueueHandle presentationQueue);
gnTextureHandle gnGetPresentationQueueImage(gnPresentationQueueHandle presentationQueue, uint32_t index);
void gnDestroyPresentationQueue(gnPresentationQueueHandle presentationQueue);

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
struct gnPlatformTexture_t; #ifdef GN_REVEAL_IMPL
struct gnTexture_t {
typedef struct gnTexture_t {
struct gnPlatformTexture_t* texture; struct gnPlatformTexture_t* texture;
} gnTexture; };
#endif