提问人:ModernEraCaveman 提问时间:9/16/2023 最后编辑:ModernEraCaveman 更新时间:9/20/2023 访问量:82
如何为 Vulkan 对象实现三法则?
How can I implement the Rule of Three for Vulkan Objects?
问:
我想创建着色器、管道、纹理和其他与 Vulkan API 一起使用的对象的简单向量,但我正在努力了解如何使用复制构造函数、移动构造函数、复制分配和移动分配操作。
我的程序构建了一个着色器结构的向量,以传递给管道进行创建:
std::vector<vk::Shader> testShaders{
{"vertex.vert", VK_SHADER_STAGE_VERTEX_BIT},
{"vertex.frag", VK_SHADER_STAGE_FRAGMENT_BIT}
};
vk::GraphicsPPL<triangleList> testPPL(testShaders);
上述方法返回一个验证层错误,其中着色器模块位于 。在另一个关于 Stack Overflow 的问题中,加载向量的解决方案是实现三法则,但我现在正在努力了解如何在不触发其他错误的情况下做到这一点。我尝试了几次使用 cpp 引用页面创建复制分配运算符和复制构造函数的尝试,但我似乎无法避免使我的程序崩溃。以下是我的一些尝试:VK_NULL_HANDLE
Shader(const Shader& other) // First attempt: copy constructor
{
std::memcpy(this, other.shaderModule, sizeof(other.shaderModule));
}// exited with code -1073741819.
Shader(const Shader& other) // Second attempt
{
vkDestroyShaderModule(GPU::device, shaderModule, nullptr);
//delete[] shaderModule; //With or without, does not change the outcome
std::memcpy(shaderModule, other.shaderModule, sizeof(other.shaderModule));
} // Couldn't find VkShaderModule Object 0xcdcdcdcdcdcdcdcd and VK_NULL_HANDLE
/* Third attempt */
Shader(const Shader& other)
: shaderStage(other.shaderStage){}
Shader& operator=(const Shader& other) // III. copy assignment
{
if (this == &other)
return *this;
VkShaderModule new_shaderModule{};
//sizeof(new_shaderModule) returns 8
//sizeof(other.shaderModule) returns 8
std::memcpy(new_shaderModule, other.shaderModule, sizeof(other.shaderModule));
vkDestroyShaderModule(GPU::device, shaderModule, nullptr); // No difference with or without, neither replacing or adding "delete[] shaderModule"
shaderModule = new_shaderModule;
return *this;
}// VK_NULL_HANDLE error
Shader(const Shader& other) // Fourth attempt - forgot to add this one
{
std::memcpy(this, &other, sizeof(Shader));
}
我的着色器结构如下:
struct Shader {
VkShaderModule shaderModule;
VkShaderStageFlagBits shaderStage;
Shader(std::string const& filename, VkShaderStageFlagBits stage)
: shaderStage(stage)
{
auto code = readFile(".\\shaders\\" + filename + ".spv"); // Function omitted from post for brevity, please don't hesitate to ask if needed
createShaderModule(code, filename);
}
~Shader() {
vkDestroyShaderModule(GPU::device, shaderModule, nullptr);
// Delete[] shaderModule // With or without, did not change the outcome
}
void createShaderModule(const std::vector<char>& code, const std::string& filename) {
VkShaderModuleCreateInfo createInfo
{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
createInfo.codeSize = code.size();
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
if (vkCreateShaderModule(GPU::device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw std::runtime_error("failed to create " + filename + "!");
}
}
/* My failed attempts here */
}
为了以防万一,Vulkan 是这样定义函数的:vkCreateShaderModule
// Provided by VK_VERSION_1_0
VkResult vkCreateShaderModule(
VkDevice device,
const VkShaderModuleCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkShaderModule* pShaderModule);
编辑:
根据注释中的建议对着色器进行添加:struct
Shader(const Shader& other) = delete;
Shader& operator=(const Shader& other) = delete;
Shader(Shader&& other) noexcept // move constructor
: shaderModule(std::exchange(other.shaderModule, nullptr)), shaderStage(std::exchange(other.shaderStage, nullptr)) {}
Shader& operator=(Shader&& other) noexcept // move assignment
{
std::swap(shaderModule, other.shaderModule);
return *this;
}
还有一些新的错误,耶......
Error C2440
'=': cannot convert from 'nullptr' to '_Ty'
/* path */ \MSVC\14.37.32822\include\utility
Error (active) E0304
no instance of function template "std::construct_at" matches the argument list
/* path */ MSVC\14.37.32822\include\xmemory
编辑2:
更改移动定义:
Shader(const Shader& other) = delete;
Shader& operator=(const Shader& other) = delete;
Shader(Shader&& other) noexcept // move constructor
{
std::swap(*this, other);
// std::exchange(*this, other); // no dice either
}
Shader& operator=(Shader&& other) noexcept // move assignment
{
std::swap(*this, other);
return *this;
}
新错误:
Error C2280 'vk::Shader::Shader(const vk::Shader &)'
attempting to reference a deleted function
/* path */\MSVC\14.37.32822\include\xutility
除了注释掉两行之外,我还尝试了解决方案,而对错误没有影响。shaderModule(nullptr)
delete
答:
我不禁认为你让自己变得比需要的更困难。“我想创建与 Vulkan API 一起使用的着色器、管道、纹理和其他对象的简单向量”——真的有必要吗?值得注意的是,每当调整容器大小时,都会在内存中移动对象。std::vector
std::vector
在构建 Vulkan 图形管道对象的情况下,您需要的是 的向量,这些向量可以存储在单个“着色器”对象中。一旦创建了管道,你就可以自由地销毁这些着色器模块。(除非管道重新创建是一回事)vkShaderModule
在我自己的 Vulkan 库中(不幸的是,不能开源 atm!我使用不透明指针来表示着色器对象,如下所示:
// shaders.hpp
struct Shader; // opaque handle defined here
Shader* create_shader(std::string_view vert, std::string_view frag);
void destroy_shader(Shader* shader);
std::vector<vkShaderModule> get_shader_modules(Shader* shader);
// shaders.cpp
struct Shader {
// definition here
};
Shader* create_shader(std::string_view vert, std::string_view frag) {
auto shader = new Shader;
// ...
return shader;
}
void destroy_shader(Shader* shader) {
// ...
delete shader;
}
// main.cpp
auto shader = create_shader("vertex.vert", "vertex.frag");
if (shader == nullptr) { /* FAIL */ }
// use shader here...
destroy_shader(shader);
shader = nullptr;
面向对象编程机制的引入有一种过于复杂的趋势,就像这里你正在努力处理移动构造函数一样,主要是因为内存分配/释放通常放在构造函数/析构函数中。在某些情况下,这是一个很好的模式,但它可能会导致许多未解决的问题。memcpy
值得注意的是,如果你现在想移动着色器手柄,将其传递给其他函数等,你只是在移动一个指针。因此,您可以消除记住通过 const ref 传递以避免复制或验证移动/复制构造函数/赋值操作是否确实被正确调用的额外复杂性。
现在这可能是主观的,但似乎有点像为了解决一个简单的问题而发明一个非常复杂的问题。(除了 Vulkan 中没有任何东西是“容易”的)。我希望这会有所帮助。
评论
std::vector
vk::Shader shaders[] = { /*list constructor 1*/, /* list constructor 2 */, ... }
评论
memcpy
this
Shader
Shader
Shader
std::shared_ptr
Shader(const Shader&)= delete;
Shader(const Shader& other) = delete;
和Shader& operator=(const Shader& other) = delete;