huge disaster with double frame buffer fixed, still no manual support but we are at least alive again

This commit is contained in:
Dennis Eichhorn 2024-12-31 09:39:49 +01:00
parent b916506f89
commit f903ac86d7
23 changed files with 2654 additions and 1631 deletions

1
.gitignore vendored
View File

@ -3,4 +3,5 @@ data/*
bin/*
*.obj
*.log
*.spv
*.res

View File

@ -27,6 +27,8 @@
#include "../platform/win32/audio/DirectSound.h"
#elif XAUDIO2
#include "../platform/win32/audio/XAudio2.h"
#elif WASAPI
#include "../platform/win32/audio/Wasapi.h"
#endif
enum AudioEffect {
@ -86,6 +88,8 @@ struct AudioMixer {
DirectSoundSetting api_setting;
#elif XAUDIO2
XAudio2Setting api_setting;
#elif WASAPI
WasapiSetting api_setting;
#endif
// @todo Replace HWND with our own typedef for linux

View File

@ -12,8 +12,11 @@
#include "../stdlib/Types.h"
#include "../math/matrix/MatrixFloat32.h"
#define SOUND_API_DIRECT_SOUND 0
#define SOUND_API_XAUDIO2 1
enum SoundApiType : byte {
SOUND_API_TYPE_DIRECT_SOUND,
SOUND_API_TYPE_XAUDIO2,
SOUND_API_TYPE_WASAPI,
};
struct AudioSetting {
f32 master_volume;
@ -30,7 +33,7 @@ struct AudioSetting {
// usually 2 * 16 = 4
byte sample_size;
// how often has the audio_play been called (required for xaudio)
// how often has the audio_play been called (required for xaudio to switch between 2 buffers)
byte sample_output;
// max buffer content/size
@ -41,10 +44,8 @@ struct AudioSetting {
uint32 sample_buffer_size;
int16* buffer;
byte type = SOUND_API_DIRECT_SOUND;
SoundApiType type;
byte latency;
// @todo add more settings e.g. repeat etc
};
struct AudioLocationSetting {

7
error/HammingCodes.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef TOS_ERROR_HAMMING_CODES_H
#define TOS_ERROR_HAMMING_CODES_H
#include "../stdlib/Types.h"
#include "../utils/BitUtils.h"
#endif

View File

@ -9,7 +9,13 @@
#ifndef TOS_GPUAPI_OPENGL_GPU_API_CONTAINER
#define TOS_GPUAPI_OPENGL_GPU_API_CONTAINER
#include "../../stdlib/Types.h"
#include "OpenglUtils.h"
struct GpuApiContainer {
uint32 frames_in_flight;
uint32 framebuffer_idx;
OpenglFrameData* framebuffers;
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29,10 +29,32 @@
#include "../../platform/linux/Window.h"
#endif
inline
void change_viewport(Window* w, int32 offset_x = 0, int32 offset_y = 0)
struct OpenglFrameData {
uint32 framebuffer;
uint32 renderbuffer;
Texture* texture;
// msaa data
uint32 framebuffer_msaa;
uint32 colorbuffer_msaa;
uint32 depthbuffer_msaa;
Texture* texture_msaa;
};
void opengl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
glViewport(offset_x, offset_y, w->width, w->height);
if (severity < GL_DEBUG_SEVERITY_LOW) {
return;
}
LOG(message, true, true);
ASSERT_SIMPLE(false);
}
inline
void change_viewport(int16 width, int16 height, int32 offset_x = 0, int32 offset_y = 0)
{
glViewport(offset_x, offset_y, width, height);
}
inline
@ -44,11 +66,7 @@ void vsync_set(int32 on)
inline
void wireframe_mode(bool on)
{
if (on) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
glPolygonMode(GL_FRONT_AND_BACK, on ? GL_LINE : GL_FILL);
}
struct OpenglInfo {
@ -144,16 +162,10 @@ void load_texture_to_gpu(const Texture* texture, int32 mipmap_level = 0)
inline
void texture_use(const Texture* texture)
{
glActiveTexture(GL_TEXTURE0 + texture->sample_id);
glBindTexture(GL_TEXTURE_2D, (GLuint) texture->id);
}
uint32 texture_data_type = get_texture_data_type(texture->texture_data_type);
// @todo should be texture_use, the Texture holds information that should make it possible to determine 1D or 2D
inline
void texture_use_1D(const Texture* texture, uint32 texture_unit)
{
glActiveTexture(GL_TEXTURE0 + texture->sample_id);
glBindTexture(GL_TEXTURE_1D, (GLuint) texture->id);
glBindTexture(texture_data_type, (GLuint) texture->id);
}
inline
@ -485,6 +497,12 @@ void gpuapi_error()
}
}
#if DEBUG
#define ASSERT_GPU_API() gpuapi_error()
#else
#define ASSERT_GPU_API() ((void) 0)
#endif
/*
void render_9_patch(GLuint texture,
int32 imgWidth, int32 imgHeight,

View File

@ -12,26 +12,12 @@
#include <windows.h>
#include "../../platform/win32/Window.h"
#include "../../stdlib/Types.h"
#include "OpenglDefines.h"
#ifdef _MSC_VER
#pragma comment(lib, "OpenGL32.Lib")
#endif
#pragma comment(lib, "OpenGL32.Lib")
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef signed char GLbyte;
typedef short GLshort;
typedef int GLint;
typedef int GLsizei;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned int GLuint;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void GLvoid;
typedef ptrdiff_t GLsizeiptr;
typedef ptrdiff_t GLintptr;
extern "C" {
WINGDIAPI void APIENTRY glAccum (GLenum op, GLfloat value);
@ -403,6 +389,15 @@ extern "C" {
typedef void WINAPI type_glTexImage2DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);
static type_glTexImage2DMultisample* glTexImage2DMultisample;
typedef GLsync WINAPI type_glFenceSync(GLenum condition, GLbitfield flags);
static type_glFenceSync* glFenceSync;
typedef GLenum WINAPI type_glClientWaitSync(GLsync GLsync, GLbitfield flags, GLuint64 timeout);
static type_glClientWaitSync* glClientWaitSync;
typedef void WINAPI type_glDeleteSync(GLsync GLsync);
static type_glDeleteSync* glDeleteSync;
typedef void WINAPI type_glBindFramebuffer(GLenum target, GLuint framebuffer);
static type_glBindFramebuffer* glBindFramebuffer;
@ -598,6 +593,10 @@ static type_glDrawElementsInstanced* glDrawElementsInstanced;
typedef void WINAPI type_glProgramParameteri(GLuint program, GLenum pname, GLint value);
static type_glProgramParameteri* glProgramParameteri;
typedef void WINAPI gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam);
typedef void WINAPI type_glDebugMessageCallback(gl_debug_callback* callback, const void* userParam);
static type_glDebugMessageCallback* glDebugMessageCallback;
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
@ -661,8 +660,6 @@ void set_pixel_format(HDC hdc, int32 multisampling = 0)
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
WGL_SAMPLE_BUFFERS_ARB, (int32) (multisampling > 0),
WGL_SAMPLES_ARB, multisampling, // MSAA
0,
};
@ -771,6 +768,9 @@ void opengl_init_wgl()
void opengl_init_gl()
{
glTexImage2DMultisample = (type_glTexImage2DMultisample *) wglGetProcAddress("glTexImage2DMultisample");
glFenceSync = (type_glFenceSync *) wglGetProcAddress("glFenceSync");
glClientWaitSync = (type_glClientWaitSync *) wglGetProcAddress("glClientWaitSync");
glDeleteSync = (type_glDeleteSync *) wglGetProcAddress("glDeleteSync");
glBindFramebuffer = (type_glBindFramebuffer *) wglGetProcAddress("glBindFramebuffer");
glGenFramebuffers = (type_glGenFramebuffers *) wglGetProcAddress("glGenFramebuffers");
glFramebufferTexture2D = (type_glFramebufferTexture2D *) wglGetProcAddress("glFramebufferTexture2D");
@ -836,6 +836,7 @@ void opengl_init_gl()
glDrawArraysInstanced = (type_glDrawArraysInstanced *) wglGetProcAddress("glDrawArraysInstanced");
glDrawElementsInstanced = (type_glDrawElementsInstanced *) wglGetProcAddress("glDrawElementsInstanced");
glProgramParameteri = (type_glProgramParameteri *) wglGetProcAddress("glProgramParameteri");
glDebugMessageCallback = (type_glDebugMessageCallback *) wglGetProcAddress("glDebugMessageCallback");
}
void opengl_destroy(Window* window)

View File

@ -9,6 +9,7 @@
#ifndef TOS_GPUAPI_VULKAN_GPU_API_CONTAINER
#define TOS_GPUAPI_VULKAN_GPU_API_CONTAINER
#include "../../stdlib/Types.h"
#include <vulkan/vulkan.h>
struct GpuApiContainer {
@ -16,13 +17,22 @@ struct GpuApiContainer {
VkSurfaceKHR surface;
VkDevice device;
VkSwapchainKHR swapchain;
uint32 swapchain_image_count;
VkFormat swapchain_image_format;
VkImage* swapchain_images; // swapchain_image_count
VkImageView* swapchain_image_views; // swapchain_image_count
VkFramebuffer* swapchain_framebuffers; // swapchain_image_count
VkExtent2D swapchain_extent;
VkPipelineLayout pipelineLayout;
VkFramebuffer framebuffer;
VkQueue graphicsQueue;
VkRenderPass renderPass;
VkQueue graphics_queue;
VkQueue present_queue;
VkRenderPass render_pass;
VkPipeline pipeline;
VkCommandPool commandPool;
VkCommandBuffer commandBuffer;
VkCommandPool command_pool;
VkCommandBuffer command_buffer;
VkSemaphore image_available_semaphore;
VkSemaphore render_finished_semaphore;
VkFence in_flight_fence;
};
#endif

View File

@ -24,8 +24,6 @@ inline uint32_t shader_get_uniform_location(VkWriteDescriptorSet* descriptor, Vk
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = descriptorType;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo->buffer = 0;
descriptorWrite.pBufferInfo->offset = 0;
}
inline void shader_set_value(VkDevice device, VkCommandBuffer commandBuffer, VkDescriptorSet descriptorSet, uint32_t binding, VkDescriptorType descriptorType, int32_t value)
@ -47,17 +45,17 @@ inline void shader_set_value(VkDevice device, VkCommandBuffer commandBuffer, VkD
vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
}
VkShaderModule shader_make(VkDevice device, const char* source, int32 source_size, RingMemory* ring)
VkShaderModule shader_make(VkDevice device, const char* source, int32 source_size)
{
// Create shader module create info
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = source_size;
createInfo.pCode = (uint32 *) source;
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.codeSize = source_size;
create_info.pCode = (uint32 *) source;
// Create shader module
VkShaderModule shaderModule;
VkResult result = vkCreateShaderModule(device, &createInfo, NULL, &shaderModule);
VkShaderModule shader_module;
VkResult result = vkCreateShaderModule(device, &create_info, NULL, &shader_module);
if (result != VK_SUCCESS) {
LOG("Failed to create shader module", true, true);
@ -66,72 +64,7 @@ VkShaderModule shader_make(VkDevice device, const char* source, int32 source_siz
return VK_NULL_HANDLE;
}
return shaderModule;
}
VkPipeline program_make(
VkDevice device,
VkShaderModule vertex_shader,
VkShaderModule fragment_shader,
VkShaderModule geometry_shader,
VkPipelineLayout pipeline_layout,
VkRenderPass render_pass,
RingMemory* ring
) {
VkPipelineShaderStageCreateInfo shaderStages[3];
// Vertex shader
shaderStages[0] = {};
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStages[0].module = vertex_shader;
shaderStages[0].pName = "main";
// Fragment shader
shaderStages[1] = {};
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = fragment_shader;
shaderStages[1].pName = "main";
VkPipelineShaderStageCreateInfo* geometryShaderStage = NULL;
// Optional Geometry shader
if (geometry_shader != VK_NULL_HANDLE) {
geometryShaderStage = &shaderStages[2];
geometryShaderStage->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
geometryShaderStage->stage = VK_SHADER_STAGE_GEOMETRY_BIT;
geometryShaderStage->module = geometry_shader;
geometryShaderStage->pName = "main";
}
// Create the pipeline
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {};
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.stageCount = (geometry_shader != VK_NULL_HANDLE) ? 3 : 2;
pipelineCreateInfo.pStages = shaderStages;
pipelineCreateInfo.pInputAssemblyState = NULL; // Define if needed
pipelineCreateInfo.pVertexInputState = NULL; // Define if needed
pipelineCreateInfo.pViewportState = NULL; // Define if needed
pipelineCreateInfo.pRasterizationState = NULL; // Define if needed
pipelineCreateInfo.pMultisampleState = NULL; // Define if needed
pipelineCreateInfo.pDepthStencilState = NULL; // Define if needed
pipelineCreateInfo.pColorBlendState = NULL; // Define if needed
pipelineCreateInfo.pDynamicState = NULL; // Define if needed
pipelineCreateInfo.layout = pipeline_layout;
pipelineCreateInfo.renderPass = render_pass;
pipelineCreateInfo.subpass = 0; // Assumes 0 subpass
pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
VkPipeline graphicsPipeline;
VkResult result = vkCreateGraphicsPipeline(device, VK_NULL_HANDLE, &pipelineCreateInfo, NULL, &graphicsPipeline);
if (result != VK_SUCCESS) {
LOG("Failed to create program", true, true);
return VK_NULL_HANDLE;
}
return graphicsPipeline;
return shader_module;
}
inline

761
gpuapi/vulkan/VulkanUtils.h Normal file
View File

@ -0,0 +1,761 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_GPUAPI_VULKAN_UTILS_H
#define TOS_GPUAPI_VULKAN_UTILS_H
#if _WIN32
#ifndef VK_USE_PLATFORM_WIN32_KHR
#define VK_USE_PLATFORM_WIN32_KHR 1
#endif
#include <vulkan/vulkan_win32.h>
#include "../../platform/win32/Window.h"
#elif __linux__
#include <vulkan/vulkan_xlib.h>
#endif
#include <vulkan/vulkan.h>
#include <vector>
#include "../../stdlib/Types.h"
#include "ShaderUtils.h"
#include "../../utils/TestUtils.h"
#include "../../log/Log.h"
#include "../../memory/RingMemory.h"
PACKED_STRUCT;
// WARNING: indices values start at one (are offset by +1) because 0 means no value in our implementation
// The reason for the packing is that sometimes we want to use it as an array
// I am only packing it on the off chance there is some funky behaviour.
struct VulkanQueueFamilyIndices {
uint32 graphics_family;
uint32 present_family;
};
UNPACKED_STRUCT;
struct VulkanSwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
uint32 format_size;
VkSurfaceFormatKHR* formats;
uint32 present_mode_size;
VkPresentModeKHR* present_modes;
};
inline
void change_viewport(Window* w, int32 offset_x = 0, int32 offset_y = 0)
{
// @todo implement
}
// @todo Implement logging of if() conditions
bool vulkan_check_validation_layer_support(const char** validation_layers, uint32 validation_layer_count, RingMemory* ring) {
uint32 layer_count;
vkEnumerateInstanceLayerProperties(&layer_count, NULL);
VkLayerProperties* available_layers = (VkLayerProperties *) ring_get_memory(ring, layer_count * sizeof(VkLayerProperties));
vkEnumerateInstanceLayerProperties(&layer_count, available_layers);
for (uint32 i = 0; i < validation_layer_count; ++i) {
bool layerFound = false;
for (uint32 j = 0; j < layer_count; ++j) {
if (strcmp(validation_layers[i], available_layers[j].layerName) == 0) {
layerFound = true;
break;
}
}
if (!layerFound) {
LOG_FORMAT("Vulkan ValidationLayer: %s\n", LOG_DATA_CHAR_STR, &validation_layers[i], true, true);
return false;
}
}
return true;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkan_debug_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT* debug_callback_data, void*
) {
LOG(debug_callback_data->pMessage, true, true);
ASSERT_SIMPLE(false);
return VK_FALSE;
}
void vulkan_populate_debug_messenger_create_info(VkDebugUtilsMessengerCreateInfoEXT* create_info)
{
create_info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info->messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
create_info->messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
create_info->pfnUserCallback = vulkan_debug_callback;
}
VkResult vulkan_debug_utils_messenger_create(
VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* create_info,
const VkAllocationCallbacks* allocator, VkDebugUtilsMessengerEXT* debug_messenger
) {
PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (!func) {
ASSERT_SIMPLE(func);
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
return func(instance, create_info, allocator, debug_messenger);
}
void vulkan_debug_messenger_setup(VkInstance instance, VkDebugUtilsMessengerEXT* debug_messenger)
{
VkDebugUtilsMessengerCreateInfoEXT create_info = {};
vulkan_populate_debug_messenger_create_info(&create_info);
if (vulkan_debug_utils_messenger_create(instance, &create_info, NULL, debug_messenger) != VK_SUCCESS) {
ASSERT_SIMPLE(false);
}
}
void vulkan_instance_create(
VkInstance* instance, const char** extensions, uint32 extension_count,
const char** validation_layers = NULL, uint32 validation_layer_count = 0,
RingMemory* ring = NULL
) {
if (validation_layer_count
&& !vulkan_check_validation_layer_support(validation_layers, validation_layer_count, ring)
) {
ASSERT_SIMPLE(false);
}
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pApplicationName = "";
app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
app_info.pEngineName = "TalesOfSouls";
app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
app_info.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
create_info.enabledExtensionCount = extension_count;
create_info.ppEnabledExtensionNames = extensions;
VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {};
if (validation_layer_count) {
create_info.enabledLayerCount = validation_layer_count;
create_info.ppEnabledLayerNames = validation_layers;
vulkan_populate_debug_messenger_create_info(&debug_create_info);
create_info.pNext = (VkDebugUtilsMessengerCreateInfoEXT *) &debug_create_info;
}
VkResult result;
if ((result = vkCreateInstance(&create_info, NULL, instance)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateInstance: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
void vulkan_surface_create(VkInstance instance, VkSurfaceKHR* surface, Window* window)
{
#if _WIN32
VkWin32SurfaceCreateInfoKHR surface_create_info = {};
surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surface_create_info.hwnd = window->hwnd;
surface_create_info.hinstance = window->hInstance;
VkResult result;
if ((result = vkCreateWin32SurfaceKHR(instance, &surface_create_info, NULL, surface)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateWin32SurfaceKHR: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
return;
}
#elif __linux__
#endif
}
bool vulkan_device_supports_extensions(VkPhysicalDevice device, const char** device_extensions, uint32 device_extension_count, RingMemory* ring) {
uint32 extension_count;
vkEnumerateDeviceExtensionProperties(device, NULL, &extension_count, NULL);
VkExtensionProperties* available_extensions = (VkExtensionProperties *) ring_get_memory(ring, extension_count * sizeof(VkExtensionProperties));
vkEnumerateDeviceExtensionProperties(device, NULL, &extension_count, available_extensions);
for (int32 i = 0; i < device_extension_count; ++i) {
bool found = false;
for (int32 j = 0; j < extension_count; ++j) {
if (strcmp(device_extensions[i], available_extensions[j].extensionName) == 0) {
found = true;
break;
}
}
if (!found) {
LOG_FORMAT("Vulkan DeviceExtensions: %s\n", LOG_DATA_CHAR_STR, &device_extensions[i], true, true);
return false;
}
}
return true;
}
VulkanQueueFamilyIndices vulkan_find_queue_families(VkPhysicalDevice physical_device, VkSurfaceKHR surface, RingMemory* ring)
{
VulkanQueueFamilyIndices indices = {};
uint32 queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, NULL);
VkQueueFamilyProperties* queue_families = (VkQueueFamilyProperties *) ring_get_memory(ring, (queue_family_count + 1) * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, queue_families);
for (int32 i = 0; i < queue_family_count; ++i) {
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphics_family = i + 1;
}
VkBool32 present_support = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface, &present_support);
if (present_support) {
indices.present_family = i + 1;
}
if (indices.graphics_family && indices.present_family) {
break;
}
}
return indices;
}
// WARNING: Since we use a RingMemory for the data, we need to copy it if we want it to be persistent
VulkanSwapChainSupportDetails vulkan_query_swap_chain_support(VkPhysicalDevice physical_device, VkSurfaceKHR surface, RingMemory* ring)
{
VulkanSwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &details.capabilities);
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details.format_size, NULL);
if (details.format_size) {
details.formats = (VkSurfaceFormatKHR *) ring_get_memory(ring, (details.format_size + 1) * sizeof(VkSurfaceFormatKHR));
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &details.format_size, details.formats);
}
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details.present_mode_size, NULL);
if (details.present_mode_size) {
details.present_modes = (VkPresentModeKHR *) ring_get_memory(ring, (details.present_mode_size + 1) * sizeof(VkPresentModeKHR));
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &details.present_mode_size, details.present_modes);
}
return details;
}
bool vulkan_is_device_suitable(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const char** device_extensions, uint32 device_extension_count, RingMemory* ring)
{
VulkanQueueFamilyIndices indices = vulkan_find_queue_families(physical_device, surface, ring);
bool extensions_supported = vulkan_device_supports_extensions(physical_device, device_extensions, device_extension_count, ring);
bool swap_chain_adequate = false;
if (extensions_supported) {
VulkanSwapChainSupportDetails swap_chain_support = vulkan_query_swap_chain_support(physical_device, surface, ring);
swap_chain_adequate = swap_chain_support.format_size && swap_chain_support.present_modes;
}
return indices.graphics_family && indices.present_family
&& extensions_supported && swap_chain_adequate;
}
// @todo Do we want to implement something similar in opengl that does something vaguely different despite not really necessary? (see wglGetGPUIDs, wglCreateAssociatedContextAMD)
void gpuapi_pick_physical_device(
VkInstance instance, VkSurfaceKHR surface, VkPhysicalDevice* physical_device,
const char** device_extensions, uint32 device_extension_count, RingMemory* ring
) {
uint32 device_count;
vkEnumeratePhysicalDevices(instance, &device_count, NULL);
VkPhysicalDevice* devices = (VkPhysicalDevice *) ring_get_memory(ring, device_count * sizeof(VkPhysicalDevice));
vkEnumeratePhysicalDevices(instance, &device_count, devices);
for (int32 i = 0; i < device_count; ++i) {
if (vulkan_is_device_suitable(devices[i], surface, device_extensions, device_extension_count, ring)) {
// @question Do we really have to do a memcpy or could we just assign? Isn't VkPhysicalDevice just a pointer internally?
memcpy(physical_device, &devices[i], sizeof(VkPhysicalDevice));
return;
}
}
LOG("Vulkan failed to find physical device\n", true, true);
ASSERT_SIMPLE(false);
}
void gpuapi_create_logical_device(
VkInstance instance, VkSurfaceKHR surface, VkDevice* device, VkPhysicalDevice physical_device,
VkQueue* graphics_queue, VkQueue* present_queue,
const char** device_extensions, uint32 device_extension_count,
const char** validation_layers, uint32 validation_layer_count, RingMemory* ring
) {
VulkanQueueFamilyIndices indices = vulkan_find_queue_families(physical_device, surface, ring);
VkDeviceQueueCreateInfo* queue_create_infos = (VkDeviceQueueCreateInfo *) ring_get_memory(ring, 2 * sizeof(VkDeviceQueueCreateInfo), 4, true);
f32 queue_priority = 1.0f;
uint32 queue_create_info_count = 1;
queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_infos[0].queueFamilyIndex = indices.graphics_family;
queue_create_infos[0].queueCount = 1;
queue_create_infos[0].pQueuePriorities = &queue_priority;
if (indices.present_family != indices.graphics_family) {
queue_create_infos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_create_infos[1].queueFamilyIndex = indices.present_family;
queue_create_infos[1].queueCount = 1;
queue_create_infos[1].pQueuePriorities = &queue_priority;
++queue_create_info_count;
}
VkPhysicalDeviceFeatures device_features = {};
VkDeviceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = queue_create_info_count;
create_info.pQueueCreateInfos = queue_create_infos;
create_info.pEnabledFeatures = &device_features;
create_info.enabledExtensionCount = device_extension_count;
create_info.ppEnabledExtensionNames = device_extensions;
if (validation_layers) {
create_info.enabledLayerCount = validation_layer_count;
create_info.ppEnabledLayerNames = validation_layers;
} else {
create_info.enabledLayerCount = 0;
}
VkResult result;
if ((result = vkCreateDevice(physical_device, &create_info, NULL, device)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateDevice: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
vkGetDeviceQueue(*device, indices.graphics_family, 0, graphics_queue);
vkGetDeviceQueue(*device, indices.present_family, 0, present_queue);
}
// WARNING: swapchain_images needs to already have reserved enough memory
// @todo How can we ensure swapchain_images has enough but not too much space?
void vulkan_swap_chain_create(
VkDevice device, VkPhysicalDevice physical_device, VkSurfaceKHR surface,
VkSwapchainKHR* swapchain, VkImage* swapchain_images, uint32* swapchain_image_count,
VkFormat* swapchain_image_format, VkExtent2D* swapchain_extent,
Window* window, RingMemory* ring
) {
VulkanSwapChainSupportDetails swap_chain_support = vulkan_query_swap_chain_support(physical_device, surface, ring);
VkSurfaceFormatKHR* surface_format = &swap_chain_support.formats[0];
for (int32 i = 0; i < swap_chain_support.format_size; ++i) {
if (swap_chain_support.formats[i].format == VK_FORMAT_B8G8R8A8_SRGB
&& swap_chain_support.formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
) {
surface_format = &swap_chain_support.formats[i];
}
}
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
for (int32 i = 0; i < swap_chain_support.present_mode_size; ++i) {
if (swap_chain_support.present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
present_mode = swap_chain_support.present_modes[i];
}
}
if (swap_chain_support.capabilities.currentExtent.width != UINT32_MAX) {
*swapchain_extent = swap_chain_support.capabilities.currentExtent;
} else {
swapchain_extent->width = OMS_CLAMP(
window->width,
swap_chain_support.capabilities.maxImageExtent.width,
swap_chain_support.capabilities.minImageExtent.width
);
swapchain_extent->height = OMS_CLAMP(
window->height,
swap_chain_support.capabilities.maxImageExtent.height,
swap_chain_support.capabilities.minImageExtent.height
);
}
uint32 image_count = swap_chain_support.capabilities.minImageCount + 1;
if (swap_chain_support.capabilities.maxImageCount > 0
&& image_count > swap_chain_support.capabilities.maxImageCount
) {
image_count = swap_chain_support.capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR create_info = {};
create_info.minImageCount = image_count;
create_info.imageFormat = surface_format->format;
create_info.imageColorSpace = surface_format->colorSpace;
create_info.imageExtent = *swapchain_extent;
create_info.imageArrayLayers = 1;
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VulkanQueueFamilyIndices indices = vulkan_find_queue_families(physical_device, surface, ring);
if (indices.graphics_family != indices.present_family) {
create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
create_info.queueFamilyIndexCount = 2;
create_info.pQueueFamilyIndices = (uint32 *) &indices;
} else {
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
create_info.preTransform = swap_chain_support.capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = present_mode;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE;
VkResult result;
if ((result = vkCreateSwapchainKHR(device, &create_info, NULL, swapchain)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateSwapchainKHR: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
vkGetSwapchainImagesKHR(device, *swapchain, swapchain_image_count, NULL);
vkGetSwapchainImagesKHR(device, *swapchain, swapchain_image_count, swapchain_images);
memcpy(swapchain_image_format, &surface_format->format, sizeof(VkFormat));
}
void vulkan_image_views_create(
VkDevice device, VkImageView* swapchain_image_views,
VkImage* swapchain_images, uint32 swapchain_image_count, VkFormat swapchain_image_format
) {
VkResult result;
for (size_t i = 0; i < swapchain_image_count; ++i) {
VkImageViewCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
create_info.image = swapchain_images[i];
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
create_info.format = swapchain_image_format;
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
create_info.subresourceRange.baseMipLevel = 0;
create_info.subresourceRange.levelCount = 1;
create_info.subresourceRange.baseArrayLayer = 0;
create_info.subresourceRange.layerCount = 1;
if ((result = vkCreateImageView(device, &create_info, NULL, &swapchain_image_views[i])) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateImageView: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
}
void create_render_pass(
VkDevice device, VkRenderPass* render_pass, VkFormat swapchain_image_format
) {
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = swapchain_image_format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
VkResult result;
if ((result = vkCreateRenderPass(device, &renderPassInfo, NULL, render_pass)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateRenderPass: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
// @todo This is very similar to program_make in opengl. Consider to rename opengl
void vulkan_pipeline_create(
VkDevice device,
VkShaderModule vertex_shader,
VkShaderModule fragment_shader,
VkShaderModule geometry_shader,
VkPipeline* pipeline,
VkPipelineLayout* pipeline_layout,
VkRenderPass render_pass,
const char* source,
int32 source_size
)
{
uint32 stage_count = 0;
VkPipelineShaderStageCreateInfo shaderStages[3];
VkPipelineShaderStageCreateInfo vs_stage_create = {};
vs_stage_create.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vs_stage_create.stage = VK_SHADER_STAGE_VERTEX_BIT;
vs_stage_create.module = vertex_shader;
vs_stage_create.pName = "main";
++stage_count;
VkPipelineShaderStageCreateInfo fs_stage_create = {};
fs_stage_create.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fs_stage_create.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fs_stage_create.module = fragment_shader;
fs_stage_create.pName = "main";
++stage_count;
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = ARRAY_COUNT(dynamicStates);
dynamicState.pDynamicStates = dynamicStates;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pushConstantRangeCount = 0;
VkResult result;
if ((result = vkCreatePipelineLayout(device, &pipelineLayoutInfo, NULL, pipeline_layout)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreatePipelineLayout: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = stage_count;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.layout = *pipeline_layout;
pipelineInfo.renderPass = render_pass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
if ((result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, pipeline)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateGraphicsPipelines: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
vkDestroyShaderModule(device, vertex_shader, NULL);
vkDestroyShaderModule(device, fragment_shader, NULL);
}
// @todo consider to rename to same name as opengl
// WARNING: swapchain_framebuffers needs to be initialized
void vulkan_framebuffer_create(
VkDevice device, VkFramebuffer* swapchain_framebuffers,
VkImageView* swapchain_image_views, uint32 swapchain_image_count, VkExtent2D swapchain_extent,
VkRenderPass render_pass
) {
VkResult result;
for (uint32 i = 0; i < swapchain_image_count; i++) {
VkImageView attachments[] = {
swapchain_image_views[i]
};
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = render_pass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = swapchain_extent.width;
framebufferInfo.height = swapchain_extent.height;
framebufferInfo.layers = 1;
if ((result = vkCreateFramebuffer(device, &framebufferInfo, NULL, &swapchain_framebuffers[i])) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateFramebuffer: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
}
void vulkan_command_pool_create(
VkDevice device, VkCommandPool* command_pool,
VkPhysicalDevice physical_device, VkSurfaceKHR surface, RingMemory* ring
) {
VulkanQueueFamilyIndices queue_family_indices = vulkan_find_queue_families(physical_device, surface, ring);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = queue_family_indices.graphics_family;
VkResult result;
if ((result = vkCreateCommandPool(device, &poolInfo, NULL, command_pool)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkCreateCommandPool: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
void vulkan_command_buffer_create(VkDevice device, VkCommandBuffer* command_buffer, VkCommandPool command_pool)
{
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = command_pool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VkResult result;
if ((result = vkAllocateCommandBuffers(device, &allocInfo, command_buffer)) != VK_SUCCESS) {
LOG_FORMAT("Vulkan vkAllocateCommandBuffers: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
void vulkan_sync_objects_create(VkDevice device, VkSemaphore* image_available_semaphore, VkSemaphore* render_finished_semaphore, VkFence* in_flight_fence)
{
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VkResult result;
if ((result = vkCreateSemaphore(device, &semaphoreInfo, NULL, image_available_semaphore)) != VK_SUCCESS
|| (result = vkCreateSemaphore(device, &semaphoreInfo, NULL, render_finished_semaphore)) != VK_SUCCESS
|| (result = vkCreateFence(device, &fenceInfo, NULL, in_flight_fence)) != VK_SUCCESS
) {
LOG_FORMAT("Vulkan vulkan_sync_objects_create: %d\n", LOG_DATA_INT32, (int32 *) &result, true, true);
ASSERT_SIMPLE(false);
}
}
/*
void vulkan_command_buffer_record(
VkCommandBuffer command_buffer
) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer(command_buffer, &beginInfo) != VK_SUCCESS) {
ASSERT_SIMPLE(false);
}
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapChainExtent;
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapChainExtent.width);
viewport.height = static_cast<float>(swapChainExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
vkCmdEndRenderPass(commandBuffer);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
ASSERT_SIMPLE(false);
}
}
*/
#endif

View File

@ -14,7 +14,7 @@
#include "Debug.h"
#ifndef MAX_LOG_LENGTH
#define MAX_LOG_LENGTH 128
#define MAX_LOG_LENGTH 256
#endif
enum LogDataType {
@ -35,6 +35,16 @@ void log(const char* format, LogDataType data_type, void* data, bool should_log,
void log_increment(int32, int64);
void log_counter(int32, int64);
#define LOG_PERFORMANCE_START(time_start) \
({ \
time_start = __rdtsc(); \
})
#define LOG_PERFORMANCE_END(time_start) \
({ \
printf("%ld\n", __rdtsc() - (time_start)); \
})
#if (!DEBUG && !INTERNAL)
// Don't perform any logging at log level 0
#define LOG(str, should_log, save) log((str), (should_log), (save), __FILE__, __func__, __LINE__)

View File

@ -13,7 +13,7 @@
#define WIN32_LEAN_AND_MEAN 1
#define NOGDICAPMASKS 1
#define NOVIRTUALKEYCODES 1
#define NOWINMESSAGES 1
//#define NOWINMESSAGES 0
#define NOWINSTYLES 1
#define NOSYSMETRICS 1
#define NOMENUS 1
@ -30,20 +30,20 @@
#define NODRAWTEXT 1
#define NOGDI 1
#define NOKERNEL 1
#define NOUSER 1
//#define NOUSER 0
#define NONLS 1
#define NOMB 1
#define NOMEMMGR 1
#define NOMETAFILE 1
#define NOMINMAX 1
#define NOMSG 1
//#define NOMSG 0
#define NOOPENFILE 1
#define NOSCROLL 1
#define NOSERVICE 1
#define NOSOUND 1
#define NOTEXTMETRIC 1
#define NOWH 1
#define NOWINOFFSETS 1
//#define NOWH 0
//#define NOWINOFFSETS 0
#define NOCOMM 1
#define NOKANJI 1
#define NOHELP 1

View File

@ -61,6 +61,7 @@ bool library_load(Library* lib)
lib->handle = LoadLibraryA((LPCSTR) dst);
if (!lib->handle) {
lib->is_valid= false;
ASSERT_SIMPLE(false);
return lib->is_valid;
}

View File

@ -24,6 +24,7 @@
#include <wbemidl.h>
#include <comdef.h>
#include <winnls.h>
#include <wingdi.h>
#include <hidsdi.h>
#if __aarch64__

View File

@ -16,6 +16,7 @@
typedef HINSTANCE WindowInstance;
struct Window {
// @question Should we implement a virtual width/height (e.g. I think apple has that where physical resolution < virtual resolution?!)
uint16 width;
uint16 height;
@ -32,9 +33,12 @@ struct Window {
HWND hwnd;
HDC hdc;
HGLRC openGLRC;
HINSTANCE hInstance;
// @bug This should only be available on opengl.
// The problem is the main program doesn't know which gpuapi we are using, so maybe a void pointer?
HGLRC openGLRC;
char name[32];
WindowState state_old;
};

View File

@ -26,7 +26,7 @@ struct DirectSoundSetting {
};
// BEGIN: Dynamically load DirectSound
typedef HRESULT WINAPI audio_create(LPCGUID, LPDIRECTSOUND8*, LPUNKNOWN);
typedef HRESULT WINAPI DirectSoundCreate8_t(LPCGUID, LPDIRECTSOUND8*, LPUNKNOWN);
HRESULT WINAPI DirectSoundCreate8Stub(LPCGUID, LPDIRECTSOUND8*, LPUNKNOWN) {
return 0;
}
@ -40,7 +40,7 @@ void audio_load(HWND hwnd, AudioSetting* setting, DirectSoundSetting* api_settin
return;
}
audio_create* DirectSoundCreate8 = (audio_create *) GetProcAddress(lib, "DirectSoundCreate8");
DirectSoundCreate8_t* DirectSoundCreate8 = (DirectSoundCreate8_t *) GetProcAddress(lib, "DirectSoundCreate8");
if (!DirectSoundCreate8 || !SUCCEEDED(DirectSoundCreate8(0, &api_setting->audio_handle, 0))) {
LOG("DirectSound: DirectSoundCreate8 failed\n", true, true);

View File

@ -0,0 +1,201 @@
/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_SOUND_WASAPI_H
#define TOS_SOUND_WASAPI_H
#include <windows.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <endpointvolume.h>
#include <commdlg.h>
#include <stdio.h>
#include "../../../stdlib/Types.h"
#include "../../../audio/AudioSetting.h"
#include "../../../utils/MathUtils.h"
#include "../../../log/Log.h"
#include "../../../audio/Audio.cpp"
struct WasapiSetting {
IAudioClient* audio_handle;
IAudioRenderClient* render_client;
};
// BEGIN: Dynamically load DirectSound
typedef HRESULT WINAPI CoInitializeEx_t(LPVOID, DWORD);
typedef HRESULT WINAPI CoCreateInstance_t(REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID*);
typedef HRESULT WINAPI IMMDeviceEnumerator_GetDefaultAudioEndpoint_t(IMMDeviceEnumerator*, EDataFlow, ERole, IMMDevice**);
typedef HRESULT WINAPI IMMDevice_Activate_t(IMMDevice*, REFIID, DWORD, PROPVARIANT*, void**);
typedef HRESULT WINAPI IAudioClient_GetMixFormat_t(IAudioClient*, WAVEFORMATEX**);
typedef HRESULT WINAPI IAudioClient_Initialize_t(IAudioClient*, AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, WAVEFORMATEX*, void*);
typedef HRESULT WINAPI IAudioClient_Start_t(IAudioClient*);
typedef HRESULT WINAPI IAudioClient_Stop_t(IAudioClient*);
typedef HRESULT WINAPI IAudioClient_GetService_t(IAudioClient*, REFIID, void**);
// END: Dynamically load DirectSound
void audio_load(HWND hwnd, AudioSetting* setting, WasapiSetting* api_setting) {
HMODULE ole32 = LoadLibraryExA((LPCSTR) "ole32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!ole32) {
LOG("Wasapi: Couldn't load ole32.dll\n", true, true);
return;
}
CoInitializeEx_t* co_initialize_ex = (CoInitializeEx_t *) GetProcAddress(ole32, "CoInitializeEx");
CoCreateInstance_t* co_create_instance = (CoCreateInstance_t *) GetProcAddress(ole32, "CoCreateInstance");
if (!co_initialize_ex || !co_create_instance) {
LOG("Wasapi: ole32 function binding failed\n", true, true);
return;
}
HMODULE mmdevapi = LoadLibraryExA((LPCSTR) "mmdevapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!mmdevapi) {
LOG("Wasapi: Couldn't load mmdevapi.dll\n", true, true);
return;
}
IMMDeviceEnumerator_GetDefaultAudioEndpoint_t* IMMDeviceEnumerator_GetDefaultAudioEndpoint = (IMMDeviceEnumerator_GetDefaultAudioEndpoint_t *) GetProcAddress(mmdevapi, "IMMDeviceEnumerator_GetDefaultAudioEndpoint");
IMMDevice_Activate_t* IMMDevice_Activate = (IMMDevice_Activate_t *) GetProcAddress(mmdevapi, "IMMDevice_Activate");
if (!IMMDeviceEnumerator_GetDefaultAudioEndpoint || !IMMDevice_Activate) {
LOG("Wasapi: mmdevapi function binding failed\n", true, true);
return;
}
HMODULE audioclient = LoadLibraryExA((LPCSTR) "audioclient.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!audioclient) {
LOG("Wasapi: Couldn't load audioclient.dll\n", true, true);
return;
}
IAudioClient_GetMixFormat_t* pIAudioClient_GetMixFormat = (IAudioClient_GetMixFormat_t *) GetProcAddress(audioclient, "IAudioClient_GetMixFormat");
IAudioClient_Initialize_t* pIAudioClient_Initialize = (IAudioClient_Initialize_t *) GetProcAddress(audioclient, "IAudioClient_Initialize");
IAudioClient_Start_t* pIAudioClient_Start = (IAudioClient_Start_t *) GetProcAddress(audioclient, "IAudioClient_Start");
IAudioClient_Stop_t* pIAudioClient_Stop = (IAudioClient_Stop_t *) GetProcAddress(audioclient, "IAudioClient_Stop");
IAudioClient_GetService_t* pIAudioClient_GetService = (IAudioClient_GetService_t *) GetProcAddress(audioclient, "IAudioClient_GetService");
if (!pIAudioClient_GetMixFormat || !pIAudioClient_Initialize || !pIAudioClient_Start || !pIAudioClient_Stop || !pIAudioClient_GetService) {
LOG("Wasapi: audioclient function binding failed\n", true, true);
return;
}
HRESULT hr = co_initialize_ex(NULL, COINIT_MULTITHREADED);
if (FAILED(hr)) {
LOG("Wasapi: Wasapi initialize failed\n", true, true);
return;
}
IMMDeviceEnumerator* enumerator;
IMMDevice* device;
hr = co_create_instance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **) &enumerator);
if (FAILED(hr)) {
LOG("Wasapi: Wasapi CreateInstance failed\n", true, true);
return;
}
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, eRender, eConsole, &device);
if (FAILED(hr)) {
LOG("Wasapi: Wasapi DefaultAudioEndpoint failed\n", true, true);
enumerator->Release();
return;
}
hr = IMMDevice_Activate(device, IID_IAudioClient, CLSCTX_ALL, NULL, (void **) &api_setting->audio_handle);
if (FAILED(hr)) {
LOG("Wasapi: Wasapi DeviceActivate failed\n", true, true);
device->Release();
enumerator->Release();
return;
}
device->Release();
enumerator->Release();
// Initializing the audio client
WAVEFORMATEX *pwfx = NULL;
api_setting->audio_handle->GetMixFormat(&pwfx);
api_setting->audio_handle->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfx, NULL);
api_setting->audio_handle->GetService(IID_IAudioRenderClient, (void **) &api_setting->render_client);
}
inline
void audio_play(AudioSetting* setting, WasapiSetting* api_setting) {
if (!api_setting->audio_handle) {
return;
}
api_setting->audio_handle->Start();
}
inline
void audio_stop(AudioSetting* setting, WasapiSetting* api_setting) {
if (!api_setting->audio_handle) {
return;
}
api_setting->audio_handle->Stop();
}
inline
void audio_free(AudioSetting* setting, WasapiSetting* api_setting)
{
if (!api_setting->render_client) {
api_setting->render_client->Release();
}
if (!api_setting->audio_handle) {
api_setting->audio_handle->Release();
}
}
inline
uint32 audio_buffer_fillable(const AudioSetting* setting, const WasapiSetting* api_setting)
{
if (!api_setting->audio_handle) {
return 0;
}
uint32 buffer_frame_count;
api_setting->audio_handle->GetBufferSize(&buffer_frame_count);
uint32 frames_padding;
api_setting->audio_handle->GetCurrentPadding(&frames_padding);
return buffer_frame_count - frames_padding;
}
inline
void audio_play_buffer(AudioSetting* setting, WasapiSetting* api_setting) {
if (!api_setting->audio_handle || setting->sample_buffer_size == 0) {
return;
}
// @question Do we have to change it from sample_buffer_size to sample count?
byte* buffer;
api_setting->render_client->GetBuffer(setting->sample_buffer_size, (byte **) &buffer);
memcpy(buffer, setting->buffer, setting->sample_buffer_size);
}
#endif

View File

@ -27,7 +27,7 @@ struct XAudio2Setting {
};
// BEGIN: Dynamically load XAudio2
typedef HRESULT WINAPI audio_create(IXAudio2**, UINT32, XAUDIO2_PROCESSOR);
typedef HRESULT WINAPI XAudio2Create_t(IXAudio2**, UINT32, XAUDIO2_PROCESSOR);
HRESULT WINAPI XAudio2CreateStub(IXAudio2**, UINT32, XAUDIO2_PROCESSOR) {
return 0;
}
@ -48,7 +48,7 @@ void audio_load(HWND hwnd, AudioSetting* setting, XAudio2Setting* api_setting) {
return;
}
audio_create* XAudio2Create = (audio_create *) GetProcAddress(lib, "XAudio2Create");
XAudio2Create_t* XAudio2Create = (XAudio2Create_t *) GetProcAddress(lib, "XAudio2Create");
if (!XAudio2Create || !SUCCEEDED(XAudio2Create(&api_setting->audio_handle, 0, XAUDIO2_DEFAULT_PROCESSOR))) {
LOG("Xaudio2: XAudio2Create failed\n", true, true);

View File

@ -19,7 +19,7 @@
#define OMS_MAX(a, b) ((a) > (b) ? (a) : (b))
#define OMS_MIN(a, b) ((a) > (b) ? (b) : (a))
#define OMS_CLAMP(a, b, c) (OMS_MAX(OMS_MIN((a), (b)), (c)))
#define OMS_CLAMP(val, high, low) (OMS_MAX(OMS_MIN((val), (high)), (low)))
#define OMS_ABS(a) ((a) > 0 ? (a) : -(a))
#define OMS_DEG2RAD(angle) ((angle) * OMS_PI / 180.0f)
#define OMS_RAD2DEG(angle) ((angle) * 180.0f / OMS_PI)

View File

@ -283,6 +283,7 @@ str_concat(
return len_total + len;
}
// @question Why is this called str_add instead of str_concat like the other functions?
inline void
str_add(char* base, const char* src)
{
@ -570,6 +571,7 @@ bool str_is_comment(char* str)
return (*str == '/' && str[1] == '/') || (*str == '/' && str[1] == '*');
}
// @question Isn't that basically like move_to? Consider to unify
inline
void str_skip(char** str, char delim)
{
@ -621,13 +623,16 @@ void str_skip_list(char** __restrict str, const char* __restrict delim, int32 le
}
inline
void str_skip_until_list(char** __restrict str, const char* __restrict delim, int32 len)
void str_skip_until_list(char** __restrict str, const char* __restrict delim)
{
while (**str != '\0') {
for (int32 i = 0; i < len; ++i) {
if (**str == delim[i]) {
const char* delim_temp = delim;
while (*delim_temp) {
if (**str == *delim_temp) {
return;
}
++delim_temp;
}
++(*str);

View File

@ -81,12 +81,9 @@
if constexpr (!(a)) { \
*(volatile int *) 0 = 0; \
}
#define ASSERT_GPU_API() gpuapi_error()
#else
#define ASSERT_SIMPLE(a) ((void) 0)
#define ASSERT_SIMPLE_CONST(a) ((void) 0)
#define ASSERT_GPU_API() ((void) 0)
#endif
#define ASSERT_TRUE(a) \
@ -119,4 +116,14 @@
} \
})
#define ASSERT_PERFORMANCE_START(time_start) \
({ \
time_start = __rdtsc(); \
})
#define ASSERT_PERFORMANCE_END(time_start, max_duration) \
({ \
ASSERT_TRUE(__rdtsc() - (time_start) <= (max_duration)); \
})
#endif