Vulkan 统一数据(内存)从 CPU 复制到 GPU 错误
Vulkan Uniform data(memory) copy from CPU to GPU error
我在从 CPU 向 GPU 端传输统一缓冲区数据时遇到问题。这似乎与 CPU/GPU 之间的内存对齐有关。我定义了一个uniform buffer对象如下:
const int MAX_LIGHTS_NUM = 32;
struct Lights {
glm::vec3 Position;
glm::vec3 Color;
float Linear;
float Quadratic;
float intensity;
};
struct UniformBufferObject {
glm::mat4 mvp;
glm::mat4 model;
glm::vec3 camPos;
int RealLightsNum;
Lights lights[MAX_LIGHTS_NUM];
};
在着色器代码中,它定义为:
layout(binding = 0) uniform UniformBufferObject {
mat4 mvp;
mat4 mMatrix;
vec3 CameraPosition;
int RealLightsNum;
Light lights[MAX_LIGHTS_NUM];
} ubo;
我定义MAX_LIGHTS_NUM为32,当我从CPU一侧复制内存时,我打印所有ubo数据并确认CPU一侧的所有数据都是正确的:
void printUBO(UniformBufferObject &ubo) {
LOGD("%s:%d Model matrix:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.model).c_str());
LOGD("%s:%d MVP matrix:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.mvp).c_str());
LOGD("%s:%d camPos:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.camPos).c_str());
LOGD("%s:%d ubo size:%d, RealLightsNum:%d", __FUNCTION__, __LINE__, sizeof(ubo), ubo.RealLightsNum);
for (int i = 0; i < ubo.RealLightsNum; i++) {
LOGD("%s:%d Light[%d].pos: %s",
__FUNCTION__, __LINE__, i, glm::to_string(ubo.lights[i].Position).c_str());
LOGD("%s:%d Light[%d].color: %s",
__FUNCTION__, __LINE__, i, glm::to_string(ubo.lights[i].Color).c_str());
LOGD("%s:%d Light[%d].(intensity/linear/quadratic: %f, %f, %f",
__FUNCTION__, __LINE__, i, ubo.lights[i].intensity, ubo.lights[i].Linear, ubo.lights[i].Quadratic);
}
}
void VulkanHal::drawPrimitive(int nodeId, unsigned int index, size_t offset, int mode, int count,
int type) {
if (initialized == false) return;
// LOGD("%s:%d nodeId:%d, index:%u offset:%u, mode:%d, count:%d, type:%d",
// __FUNCTION__, __LINE__, nodeId, index, offset, mode, count, type);
// LOGD("%s:%d -", __FUNCTION__, __LINE__);
// LOGD("%s:%d ubo size:%d, RealLightsNum:%d", __FUNCTION__, __LINE__, sizeof(ubo), ubo.RealLightsNum);
printUBO(ubo);
int primIndex = getPrimitiveIndex(nodeId, index);
void* data;
vkMapMemory(device, uniformBuffersMemory[imageIndex][primIndex], 0, sizeof(ubo), 0, &data);
memcpy(data, &ubo, sizeof(ubo));
vkUnmapMemory(device, uniformBuffersMemory[imageIndex][primIndex]);
}
我截取了renderdoc ubo数据,发现RealLightsNum之前的数据都是正确的,但是Light数据只有第一个是部分正确的,也就是说位置是正确的,但是颜色第三通道是不正确的。
我怀疑这是内存对齐问题,但无法正确解决。有人可以帮忙吗,在此先感谢。
日志:
renderdoc 捕获:
我在 link Vulkan Memory Alignment for Uniform Buffers 上得到了答案。它说对于 CPU 侧的每个元素,我们应该设置对齐方式:
- a int/float 对齐 4
- 一个 vec2 的对齐方式为 8
- 一个vec3, vec4, mat4 16的排列
我设置后就生效了:
const int MAX_LIGHTS_NUM = 32;
struct Lights {
alignas(16) glm::vec3 Position;
alignas(16) glm::vec3 Color;
alignas(4) float Linear;
alignas(4) float Quadratic;
alignas(4) float intensity;
};
struct UniformBufferObject {
alignas(16) glm::mat4 mvp;
alignas(16) glm::mat4 model;
alignas(16) glm::vec3 camPos;
alignas(4) int RealLightsNum;
Lights lights[MAX_LIGHTS_NUM];
};
我在从 CPU 向 GPU 端传输统一缓冲区数据时遇到问题。这似乎与 CPU/GPU 之间的内存对齐有关。我定义了一个uniform buffer对象如下:
const int MAX_LIGHTS_NUM = 32;
struct Lights {
glm::vec3 Position;
glm::vec3 Color;
float Linear;
float Quadratic;
float intensity;
};
struct UniformBufferObject {
glm::mat4 mvp;
glm::mat4 model;
glm::vec3 camPos;
int RealLightsNum;
Lights lights[MAX_LIGHTS_NUM];
};
在着色器代码中,它定义为:
layout(binding = 0) uniform UniformBufferObject {
mat4 mvp;
mat4 mMatrix;
vec3 CameraPosition;
int RealLightsNum;
Light lights[MAX_LIGHTS_NUM];
} ubo;
我定义MAX_LIGHTS_NUM为32,当我从CPU一侧复制内存时,我打印所有ubo数据并确认CPU一侧的所有数据都是正确的:
void printUBO(UniformBufferObject &ubo) {
LOGD("%s:%d Model matrix:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.model).c_str());
LOGD("%s:%d MVP matrix:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.mvp).c_str());
LOGD("%s:%d camPos:%s", __FUNCTION__, __LINE__, glm::to_string(ubo.camPos).c_str());
LOGD("%s:%d ubo size:%d, RealLightsNum:%d", __FUNCTION__, __LINE__, sizeof(ubo), ubo.RealLightsNum);
for (int i = 0; i < ubo.RealLightsNum; i++) {
LOGD("%s:%d Light[%d].pos: %s",
__FUNCTION__, __LINE__, i, glm::to_string(ubo.lights[i].Position).c_str());
LOGD("%s:%d Light[%d].color: %s",
__FUNCTION__, __LINE__, i, glm::to_string(ubo.lights[i].Color).c_str());
LOGD("%s:%d Light[%d].(intensity/linear/quadratic: %f, %f, %f",
__FUNCTION__, __LINE__, i, ubo.lights[i].intensity, ubo.lights[i].Linear, ubo.lights[i].Quadratic);
}
}
void VulkanHal::drawPrimitive(int nodeId, unsigned int index, size_t offset, int mode, int count,
int type) {
if (initialized == false) return;
// LOGD("%s:%d nodeId:%d, index:%u offset:%u, mode:%d, count:%d, type:%d",
// __FUNCTION__, __LINE__, nodeId, index, offset, mode, count, type);
// LOGD("%s:%d -", __FUNCTION__, __LINE__);
// LOGD("%s:%d ubo size:%d, RealLightsNum:%d", __FUNCTION__, __LINE__, sizeof(ubo), ubo.RealLightsNum);
printUBO(ubo);
int primIndex = getPrimitiveIndex(nodeId, index);
void* data;
vkMapMemory(device, uniformBuffersMemory[imageIndex][primIndex], 0, sizeof(ubo), 0, &data);
memcpy(data, &ubo, sizeof(ubo));
vkUnmapMemory(device, uniformBuffersMemory[imageIndex][primIndex]);
}
我截取了renderdoc ubo数据,发现RealLightsNum之前的数据都是正确的,但是Light数据只有第一个是部分正确的,也就是说位置是正确的,但是颜色第三通道是不正确的。
我怀疑这是内存对齐问题,但无法正确解决。有人可以帮忙吗,在此先感谢。
日志:
renderdoc 捕获:
我在 link Vulkan Memory Alignment for Uniform Buffers 上得到了答案。它说对于 CPU 侧的每个元素,我们应该设置对齐方式:
- a int/float 对齐 4
- 一个 vec2 的对齐方式为 8
- 一个vec3, vec4, mat4 16的排列
我设置后就生效了:
const int MAX_LIGHTS_NUM = 32;
struct Lights {
alignas(16) glm::vec3 Position;
alignas(16) glm::vec3 Color;
alignas(4) float Linear;
alignas(4) float Quadratic;
alignas(4) float intensity;
};
struct UniformBufferObject {
alignas(16) glm::mat4 mvp;
alignas(16) glm::mat4 model;
alignas(16) glm::vec3 camPos;
alignas(4) int RealLightsNum;
Lights lights[MAX_LIGHTS_NUM];
};