-
Notifications
You must be signed in to change notification settings - Fork 27
Simple game engine #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Simple game engine #119
Conversation
…y commit to start getting proofreading while I still iterate on getting the engine to work. Engine still needs assets. Engine still needs Android project created. Engine currently crashes on running. Add `SimpleEngine` with Entity-Component-System (ECS) implementation - Introduced the `SimpleEngine` project featuring a modular ECS-based architecture. - Implemented core files: `Entity`, `Component`, and `TransformComponent` for managing entities and transformations. - Added CMake setup with Vulkan, GLFW, GLM, TinyGLTF, and KTX dependencies for rendering support. - Integrated shader compilation workflow using `slangc`. - Included initial scene setup with basic camera and cube entities in `main.cpp`.
…mmendations - Consolidated sections to address vendor-agnostic optimizations for mobile GPUs. - Replaced Huawei-specific details with broader guidance applicable to various mobile GPU architectures (Mali, Adreno, PowerVR, etc.). - Improved Vulkan extension examples and added checks for multiple vendor IDs for better compatibility. - Updated best practices and performance tuning sections to emphasize device diversity and testing requirements.
…n details - Simplified and reorganized GPU optimization lists for readability. - Expanded explanations for `VK_KHR_dynamic_rendering_local_read` and `VK_EXT_shader_tile_image` to highlight memory bandwidth reduction benefits. - Improved consistency in vendor-specific and tile-based optimization recommendations. - Added detailed examples and practical use cases for extension advantages in mobile rendering pipelines.
- Expanded tutorial with a comprehensive explanation of rendergraphs, including resource management, dependency tracking, and synchronization benefits. - Added a sample `Rendergraph` class implementation with methods for resource and pass management, including compile and execute functions. - Included practical examples such as deferred renderer setup and Vulkan synchronization best practices using semaphores, fences, and pipeline barriers. - Enhanced Vulkan tutorial content with a focus on simplifying complex rendering workflows and optimizing synchronization.
- Inserted new diagrams for layered architecture, component-based architecture, data-oriented design, and service locator pattern in the engine tutorial. - Adjusted and reordered content in corresponding sections for improved clarity and structure. - Updated references to foundational concepts, ensuring consistency across sections.
- Included a detailed clarification about windowing libraries and their purpose. - Provided examples of popular windowing libraries like GLFW, SDL, Qt, and SFML. - Enhanced understanding of platform-specific abstraction benefits for developers.
…i needs some help. it also looks like cleanup logic and resizing needs work.
…the semaphore. resizing still doesn't work. Next step is to get the parts of the engine to demonstrate things.
…the semaphore. resizing still doesn't work. Next step is to get the parts of the engine to demonstrate things.
…U processing mode.
…nk that's due to material only looking at the vespa. This includes camera controls.
Also need to check all external links. Afair already mentioned that and there are still links that are either wrong or just dead like https://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch17.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log level is just some kind of categorization with the generated output, you can't filter for e.g. warnings and above, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, although I might change that. I've been debating about doing that change. Might make things easier for people wanting to get used to Android development to have it work the same everywhere.
|
||
// Convenience macros for performance measurement | ||
#define MEASURE_START(name) DebugSystem::GetInstance().StartMeasurement(name) | ||
#define MEASURE_END(name) DebugSystem::GetInstance().StopMeasurement(name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MEASURE_END
-> MEASURE_STOP
?
/** | ||
* @brief Clean up engine resources. | ||
*/ | ||
void Cleanup(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Cleanup
called from somewhere else than the destructor?
// Initialize per-frame buffers containers | ||
if (renderer) { | ||
uint32_t frames = renderer->GetMaxFramesInFlight(); | ||
vertexBuffers.clear(); vertexBuffers.reserve(frames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the vertexBuffers
be non-empty before reserving here?
Maybe an assert(vertexBuffers.empty());
would be appropriate, instead.
|
||
// Bind vertex and index buffers for this frame | ||
std::array<vk::Buffer, 1> vertexBuffersArr = {*vertexBuffers[frameIndex]}; | ||
std::array<vk::DeviceSize, 1> offsets = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you use std::array
s of size 1, instead of just one value?
samplerInfo.minLod = 0.0f; | ||
samplerInfo.maxLod = 0.0f; | ||
samplerInfo.borderColor = vk::BorderColor::eFloatOpaqueWhite; | ||
samplerInfo.unnormalizedCoordinates = VK_FALSE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use designated initializers and use default values for irrelevant settings here?
vk::PushConstantRange pushConstantRange; | ||
pushConstantRange.stageFlags = vk::ShaderStageFlagBits::eVertex; | ||
pushConstantRange.offset = 0; | ||
pushConstantRange.size = sizeof(float) * 4; // 2 floats for scale, 2 floats for translate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you have some struct holding this data?
if (frameIndex >= vertexCounts.size()) return; // Safety | ||
|
||
if (drawData->TotalVtxCount > vertexCounts[frameIndex]) { | ||
// Clean up old buffer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you explicitly clean up the old buffers? Aren't they automatically cleaned when you assign some new buffers?
|
||
vertexBufferMemories[frameIndex] = vk::raii::DeviceMemory(device, allocInfo); | ||
vertexBuffers[frameIndex].bindMemory(*vertexBufferMemories[frameIndex], 0); | ||
vertexCounts[frameIndex] = drawData->TotalVtxCount; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should either check for drawData->TotalVtxCount != vertexCounts[frameIndex]
, or move this assignment out of this block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments...
@@ -23,6 +24,7 @@ | |||
*/ | |||
class Engine { | |||
public: | |||
using TimeDelta = std::chrono::milliseconds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throughout the code, you're sometimes using TimeDelta
, and sometimes using std::chrono::milliseconds
. Maybe you should use just one of them?
components.erase(vecIt); | ||
for (auto it = components.rbegin(); it != components.rend(); ++it) { | ||
if (dynamic_cast<T*>(it->get()) != nullptr) { | ||
components.erase(std::next(it).base()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you erase std::next(it)
, not it
?
|
||
// Pre-allocate at least one block for each pool type | ||
for (const auto& [poolType, config] : poolConfigs) { | ||
auto poolIt = pools.find(poolType); | ||
if (poolIt == pools.end()) { | ||
pools[poolType] = std::vector<std::unique_ptr<MemoryBlock>>(); | ||
poolIt = pools.find(poolType); | ||
poolIt = pools.try_emplace( poolType ).first; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No find and check needed, just try_emplace
an empty pool.
auto it = models.find(filename); | ||
if (it != models.end()) { | ||
return it->second.get(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it reasonable to cache the models in the ModelLoader
?
std::cout << "Blender generator detected, applying blender factor" << std::endl; | ||
light_scale = EMISSIVE_SCALE_FACTOR; | ||
} | ||
light_scale = EMISSIVE_SCALE_FACTOR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
light_scale
is EMISSIVE_SCALE_FACTOR
, no matter what generator is used?
return emptyVector; | ||
} | ||
|
||
Material* ModelLoader::GetMaterial(const std::string& materialName) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A const
function should return a const *
.
light.type = ExtractedLight::Type::Point; | ||
} else if (type == "spot") { | ||
light.type = ExtractedLight::Type::Spot; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if type
is none of "directional", "point", or "spot"?
colorArray[1].IsNumber() ? static_cast<float>(colorArray[1].Get<double>()) : 1.0f, | ||
colorArray[2].IsNumber() ? static_cast<float>(colorArray[2].Get<double>()) : 1.0f | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No default color?
// Compute world transforms for all nodes in the default scene | ||
std::vector<glm::mat4> nodeWorldTransforms(gltfModel.nodes.size(), glm::mat4(1.0f)); | ||
|
||
auto calcLocal = [](const tinygltf::Node& n) -> glm::mat4 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You already had the very same lambda, but called it calculateNodeTransform
up at line 668.
} | ||
|
||
// Now assign positions and directions using world transforms | ||
for (size_t nodeIndex = 0; nodeIndex < gltfModel.nodes.size(); ++nodeIndex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, what's the expected node to light node ratio.
Is it worth to calculate the world transform for each node, and then use those from light nodes only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And some more comments...
*/ | ||
struct MaterialMesh { | ||
int materialIndex; | ||
std::string materialName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The materialndex
is the index into the gltfModel::materials
, and the materialName
is the key for the ModelLoader::materials
, right?
Why don't you hold the ModelLoader::materials
as an array, corresponding to the gltfModel::materials
? Then you would just need that index.
std::string normalTexturePath; // Normal map texture | ||
std::string metallicRoughnessTexturePath; // Metallic-roughness texture | ||
std::string occlusionTexturePath; // Ambient occlusion texture | ||
std::string emissiveTexturePath; // Emissive texture |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are all those texture paths related to those in the Material
, referenced by the materialName
?
|
||
// Instancing support | ||
std::vector<InstanceData> instances; // Instance data for instanced rendering | ||
bool isInstanced = false; // Flag to indicate if this mesh uses instancing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it simplify things if you added some default InstanceData
for the not instanced meshes?
Resulting in a one-element vector for the non-instanced meshes, and a multi-element vector for the instanced ones.
* @param transform The transform matrix for this instance. | ||
* @param matIndex The material index for this instance (default: use materialIndex). | ||
*/ | ||
void AddInstance(const glm::mat4& transform, uint32_t matIndex = 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should matIndex
be renamed to materialIndex
?
In the context of matrix handling, matIndex
might be misleading.
*/ | ||
void AddInstance(const glm::mat4& transform, uint32_t matIndex = 0) { | ||
if (matIndex == 0) matIndex = static_cast<uint32_t>(materialIndex); | ||
instances.emplace_back(transform, matIndex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and why have the instances
a materialIndex
?
I would have expected that everything in a MaterialMesh is related to the very same material.
Maybe you should decouple Mesh (vertices and indices) and Material?
.setDstArrayElement(0) | ||
.setDescriptorCount(1) | ||
.setDescriptorType(vk::DescriptorType::eUniformBuffer) | ||
.setPBufferInfo(¶msBufferInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Designated initializers, maybe?
vulkanResources.collisionBufferMemory = nullptr; | ||
vulkanResources.physicsBuffer = nullptr; | ||
vulkanResources.physicsBufferMemory = nullptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all those resources are ordered correctly in the vulkanResources
, you would just need to waitForIdle
and to unmap
the memories. All others would be automatically done.
// Validate Vulkan resources and persistent memory pointers before using them | ||
if (*vulkanResources.physicsBuffer == VK_NULL_HANDLE || *vulkanResources.physicsBufferMemory == VK_NULL_HANDLE || | ||
*vulkanResources.counterBuffer == VK_NULL_HANDLE || *vulkanResources.counterBufferMemory == VK_NULL_HANDLE || | ||
*vulkanResources.paramsBuffer == VK_NULL_HANDLE || *vulkanResources.paramsBufferMemory == VK_NULL_HANDLE || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of comparing with VK_NULL_HANDLE, you could as well check for !*vulkanResources.physicsBuffer
, etc.
concreteRigidBody->GetRotation().z, concreteRigidBody->GetRotation().w); | ||
gpuData[i].linearVelocity = glm::vec4(concreteRigidBody->GetLinearVelocity(), concreteRigidBody->GetRestitution()); | ||
gpuData[i].angularVelocity = glm::vec4(concreteRigidBody->GetAngularVelocity(), concreteRigidBody->GetFriction()); | ||
// CRITICAL FIX: Initialize forces properly instead of always resetting to zero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this comment mean?
You still initialize with zero.
switch (concreteRigidBody->GetShape()) { | ||
case CollisionShape::Sphere: | ||
// Use tennis ball radius (0.0335f) instead of hardcoded 0.5f | ||
gpuData[i].colliderData = glm::vec4(0.0335f, 0.0f, 0.0f, static_cast<float>(0)); // 0 = Sphere |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again this hardcoded 0.0335f
.
Maybe at least some constexpr
somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments...
/** | ||
* @brief Class representing a rigid body for physics simulation. | ||
*/ | ||
class RigidBody { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, there's just one class deriving from RigidBody
: ConcreteRigidBody
.
Do you plan to introduce more classes later on, or why do you introduce this pure virtual base class?
* @param rigidBody The rigid body to remove. | ||
* @return True if removal was successful, false otherwise. | ||
*/ | ||
bool RemoveRigidBody(RigidBody* rigidBody); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the RigidBody
removed from?
As it's the counterpart to CreateRigidBody
, you might name it DestroyRigidBody
?
However, one could argue whether creating a RigidBody
should be part of the PhysicsSystem
at all. Maybe you should instead change CreateRigidBody
to AddRigidBody(std::unique_ptr<RigidBody> rb);
.
|
||
// Destructor | ||
Pipeline::~Pipeline() { | ||
// RAII will handle destruction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the destructor does nothing, you just don't need it.
} | ||
|
||
// Create descriptor set layout | ||
bool Pipeline::createDescriptorSetLayout() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the alternative to createPBRDescriptorSetLayout
?
Then you might name it more specific, like createPhongDescriptorSetLayout
?
|
||
// Create shader modules | ||
vk::raii::ShaderModule vertShaderModule = createShaderModule(vertShaderCode); | ||
vk::raii::ShaderModule fragShaderModule = createShaderModule(fragShaderCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are two separate shader modules out of two separate but identical shader codes necessary?
.depthBiasClamp = 0.0f, | ||
.depthBiasSlopeFactor = 0.0f, | ||
.lineWidth = 1.0f | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said above: no need to set members to their default value.
|
||
// Create shader modules | ||
vk::raii::ShaderModule vertShaderModule = createShaderModule(vertShaderCode); | ||
vk::raii::ShaderModule fragShaderModule = createShaderModule(fragShaderCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again: two separate shader modules out of two separate but identical shader codes?
.pName = "PSMain" // Changed from FSMain to PSMain to match the shader | ||
}; | ||
|
||
vk::PipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again: use a std::array
?
.viewportCount = 1, | ||
.pViewports = &viewport, | ||
.scissorCount = 1, | ||
.pScissors = &scissor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again: no need to set pViewports
and pScissors
for dynamic viewport and scissor
std::cerr << "Failed to create lighting pipeline: " << e.what() << std::endl; | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createGraphicsPipeline
, createPBRPipeline
, and createLightingPipeline
are some lengthy functions that in large parts are identical. Worth to add some helper function(s)?
@gpx1000 : Still can't build this on Windows with MSVC:
And some more errors. Would like to run this at least once for a review. Any idea on how to fix? |
I haven't touched windows compiling yet. I'll try to get that addressed ASAP. |
To get it compile with MSVC I did this:
|
That explains it. looks like the KTX2 file being missing and it's using the hand coded default files. That's the root cause of the no colors. The audio, select default ping and hit play. You should hear something. I don't think I checked in the wav I've been using for debug as I don't think that's a good one to use. Validation is clean here. give me a sample of them. frame rate here is around 16fps. The intention is to have culling be part of the samples version of the game engine but that wasn't introduced here for this one so while I want to do things like improve the loading speed of the model, I'm trying to hold off and keep it as simple as possible yet be something we can build off of for the samples version. I chose optimization as a topic for the Samples version when it comes to getting it to work in Android / iOS. |
Grab the latest of the bistro and you should have textures. |
@SaschaWillems if you want, the changes you did for Windows, please make a PR against my branch here and I'll merge in your changes. That way we can keep a history and enable any further help you might be willing to give. |
For what it's worth, the model you're using is slightly different. You're using a version of the bistro that's a lot simpler in that it doesn't come from the NVIDIA one that opens up the bistro and lets you go inside. There's also other differences. But I think a key one is the blender specified lumans value which does make the scene too bright during the noon day. Yes, there's still plenty of bugs in the rendering that I'm still trying to work out. But I'm still not entirely sure how much of it is due to rendering bugs from the punctual light source (sun) vs not. And the time of day slider is more for debugging the PBR pipeline. I'm still struggling with a few things in it. The phong version doesn't have the slider support because I haven't found a need to validate it there yet, but maybe it makes sense to keep the slider as it is pretty neat and add support to the phong path. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments...
|
||
// Call the resize callback if set | ||
if (platform->resizeCallback) { | ||
platform->resizeCallback(platform->width, platform->height); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code for APP_CMD_INIT_WINDOW
and APP_CMD_WINDOW_RESIZED
are identical?
|
||
bool AndroidPlatform::HasWindowResized() { | ||
bool resized = windowResized; | ||
windowResized = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correct that a query (HasWindowResized
) is not const
and potentially modifies the queried state?
|
||
void AndroidPlatform::SetWindowTitle(const std::string& title) { | ||
// No-op on Android - mobile apps don't have window titles | ||
(void)title; // Suppress unused parameter warning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use [[maybe_unused]]
instead?
env->DeleteLocalRef(serviceStr); | ||
|
||
// Check Vulkan support | ||
// In a real implementation, this would check for Vulkan support and available extensions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And in this implementation, you don't do that because of?
* @brief Get the Vulkan RAII device. | ||
* @return The Vulkan RAII device. | ||
*/ | ||
const vk::raii::Device& GetRaiiDevice() const { return device; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth to have both, GetDevice()
and GetRAIIDevice()
?
Wouldn't getting the vk::raii::Device const &
be sufficient, as you get to the corresponding vk::Device
that easy?
* @param commandBuffer The command buffer to submit. | ||
* @param fence The fence to signal when the operation completes. | ||
*/ | ||
void SubmitToComputeQueue(vk::CommandBuffer commandBuffer, vk::Fence fence) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep the API consistent, you could pass in a vk::raii::CommandBuffer const &
and a vk::raii::Fence const &
here.
* @param oldLayout The old layout. | ||
* @param newLayout The new layout. | ||
*/ | ||
void TransitionImageLayout(vk::Image image, vk::Format format, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass in a vk::raii::Image const &
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this file, some functions get/return a vk::Handle, some get/return a vk::raii::Handle.
Maybe it would be more consistent to always use a vk::raii::Handle.
std::vector<vk::DescriptorSet> descriptorSetsToBindRaw; | ||
descriptorSetsToBindRaw.reserve(1); | ||
descriptorSetsToBindRaw.push_back(*computeDescriptorSets[0]); | ||
commandBufferRaii.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *computePipelineLayout, 0, descriptorSetsToBindRaw, {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to copy stuff into a one-element vector.
You could just call
commandBufferRaii.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *computePipelineLayout, 0, *computeDescriptorSets[0], {});
// Extract raw command buffer for submission and release RAII ownership | ||
// This prevents premature destruction while preserving the recorded commands | ||
vk::CommandBuffer rawCommandBuffer = *commandBufferRaii; | ||
commandBufferRaii.release(); // Release RAII ownership to prevent destruction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where will the rawCommandBuffer
be destroyed, then?
…lkan queue handling. Bistro.gltf loads all textures, and creates physics geometry in under 44 seconds. Seeing 55 fps as well.
A code related question, that Andreas might've already asked (hard to see, github hides older comments): What is the idea behind converting (Vulkan-hpp) exceptions into bools? See e.g. Since this is exclusively using Vulkan-hpp and C++, why not stick to exceptions. So in above case, try, catch, log inside the function and raise the exception up to the calling code. |
This is the next big tutorial effort. I added the conclusion to the base line tutorial, and this is the "next thing." The game engine presented here is one example of what the tutorial describes and not a 1-1 mapping of the source code to the implementation in the attachments folder.
Future work will add new features to the tutorial in the Vulkan Samples project. This is meant to give the course to describe basics of the game engine and Samples is meant to describe features as they are implemented. This can be used as a template for a starting off point for any project as it is designed to be self contained.
Core features in this tutorial/engine:
Several features are left out intentionally as a method of what we'll implement and feature in the Samples including parallel loading of the assets. Bistro takes a few moments to load as it stands here.