2017-11-10 3 views
2

Ich möchte ein einheitliches Pufferobjekt mit zwei 4x4-Matrizen an den Vertex-Shader übergeben. Ich habe die Struktur in C++ wie folgt deklariert:Vulkan UBO falsch übergeben

struct PerRenderUBO 
{ 
    glm::mat4 viewProjection; 
    glm::mat4 projection; //unused 
}; 

Und in GLSL:

layout(std140, set = 0, binding = 0) uniform UBO { 
    mat4 viewProjection; 
    mat4 projection; //unused 
} perRenderUBO; 

aber sobald ich mehr als ein Mitglied in meinem UBO struct erklären, bekommen einige Objekte gezeichnet falsch durch zu einer falschen SichtProjektionsmatrix. Wenn ich den Datenelement 'projektion' sowohl in der PerRenderUBO-Struktur als auch in der GLSL-Deklaration auskommentiere, wird alles korrekt gerendert (obwohl ich die Projektion nicht einmal im Shader verwendet habe).

Dies führt zu der Annahme, dass mit der Datenausrichtung etwas nicht in Ordnung ist. Ich habe das Layout mit der Annotation std140 deklariert. sizeof (PerRenderUBO) gibt 128 zurück. Ich habe versucht, 'projection' als float zu deklarieren und das Problem bleibt bestehen.

Hier sind die wichtigsten Teile des Deskriptors Satzerstellung:

/** Create Layout **/ 
vk::DescriptorSetLayoutBinding perRenderUBOBinding; 
perRenderUBOBinding.binding = 0; 
perRenderUBOBinding.descriptorCount = 1; 
perRenderUBOBinding.descriptorType = vk::DescriptorType::eUniformBuffer; 
perRenderUBOBinding.stageFlags = vk::ShaderStageFlagBits::eVertex; 
perRenderUBOBinding.pImmutableSamplers = nullptr; 

std::vector<vk::DescriptorSetLayoutBinding> bindings{perRenderUBOBinding}; 

vk::DescriptorSetLayoutCreateInfo createInfo; 
createInfo.bindingCount = bindings.size(); 
createInfo.pBindings = bindings.data(); 
this->perRenderUBOLayout = vkDevice->createDescriptorSetLayout(createInfo); 

/** Create host-visible and host-coherent buffer **/ 
vk::BufferCreateInfo bufferCreateInfo; 
bufferCreateInfo.size = sizeof(PerRenderUBO); 
bufferCreateInfo.usage = vk::BufferUsageFlagBits::eUniformBuffer; 
bufferCreateInfo.sharingMode = vk::SharingMode::eExclusive; 
bufferCreateInfo.queueFamilyIndexCount = queueFamilyIndices.size(); 
bufferCreateInfo.pQueueFamilyIndices = queueFamilyIndices.data(); 
this->buffer = vkDevice->createBuffer(bufferCreateInfo); 

vk::MemoryRequirements memoryRequirements = vkDevice->getBufferMemoryRequirements(this->buffer); 
//allocates device memory as proposed in the specification (10.2 Device Memory) 
this->bufferDeviceMemory = allocate(memoryRequirements, vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eHostVisible} | vk::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eHostCoherent}); 
vkDevice->bindBufferMemory(this->buffer, this->bufferDeviceMemory, 0); 


this->descriptorPool = ...//create a descriptor pool for 1 uniform buffer 
this->descriptorSet = ...//allocate descriptor set for above layout 

vk::DescriptorBufferInfo bufferInfo; 
bufferInfo.buffer = this->buffer; 
bufferInfo.offset = 0; 
bufferInfo.range = sizeof(PerRenderUBO); 

vk::WriteDescriptorSet writeDescriptorSet; 
writeDescriptorSet.dstSet = *this->descriptorSet; 
writeDescriptorSet.dstBinding = 0; 
writeDescriptorSet.dstArrayElement = 0; 
writeDescriptorSet.descriptorType = vk::DescriptorType::eUniformBuffer; 
writeDescriptorSet.descriptorCount = 1; 
writeDescriptorSet.pBufferInfo = &bufferInfo; 
writeDescriptorSet.pImageInfo = nullptr; 
writeDescriptorSet.pTexelBufferView = nullptr; 

vkDevice->updateDescriptorSets({writeDescriptorSet}, {}); 

den Primärzeichenbefehles Puffer vor der Ausführung I der Puffer für die PerRenderUBO wie folgt aktualisieren:

std::vector<PerRenderUBO> data; //contains 1 instance of PerRenderUBO 
vk::DeviceSize offset = 0; 
vk::DeviceSize size = data.size() * sizeof(PerRenderUBO); 

void* memory = vkDevice->mapMemory(this->bufferDeviceMemory, offset, size); 
std::memcpy(memory, data.data(), size); 
this->deviceMemory->unmap(); 

I‘ habe Puffergrößen und Offsets mehrmals überprüft und alles sieht gut aus. Aufgrund der Tatsache, dass der gleiche Deskriptorsatz für jeden Zeichenbefehl gebunden ist und einige Objekte korrekt dargestellt werden, glaube ich, dass die Daten im Puffer selbst korrekt sein müssen. Was fehlt mir?

+0

Können Sie auch den Teil hinzufügen, der die Ubo-Daten tatsächlich hochlädt? –

+0

Ich habe den Code für die Puffererstellung und für das Pufferupdate hinzugefügt. – user3067395

+0

Sie haben nur eine PerRenderUBO-Instanz, sprechen aber über das Rendern mehrerer Objekte. Haben Sie nur eine Zeichnung pro Befehlspuffer (da Sie/memcpy/unmap kurz vor dem Senden des Befehlspuffers abbilden)? Wenn ja, was stellt sicher, dass Sie den UBO-Inhalt für die Zeichnung N + 1 nicht überschreiben, bevor die Zeichnung N abgeschlossen ist? –

Antwort

1

Die Tatsache, dass einige Objekte falsch und einige korrekt gezeichnet werden, könnte auf ein Problem mit der Synchronisierung hinweisen oder dazu führen, dass der Puffer nicht rechtzeitig aktualisiert wird.

Zuerst müssen Sie dem Treiber mitteilen, welche Teile des Puffers vom Host aktualisiert wurden. Ich sehe keinen solchen Code und Sie erwähnen nichts darüber. Dies geschieht durch Spülen eines Speichers - Sie müssen die Funktion vkFlushMappedMemoryRanges() aufrufen.

Möglicherweise müssen Sie auch eine Schranke einrichten, die den Treiber darüber informiert, dass auf den Puffer vom Host zugegriffen wurde. Aber soweit ich mich erinnere, ist eine solche Barriere implizit auf die Übergabe des Befehlspuffers gesetzt.

+0

Vielen Dank! Ich hätte präzisieren müssen, dass ich einen host-kohärenten Speicher zuweist, der keinen Aufruf von vkFlushMappedMemoryRanges() erfordern sollte. Es könnte jedoch ein Problem mit der Synchronisation geben. Ich habe versucht, die Daten für den Puffer nur einmal einzustellen, so dass nach der Erstellung des Puffers keine Schreiboperationen durchgeführt werden. Leider konnte das Problem dadurch nicht behoben werden. Ich habe auch versucht, die PerRenderUBO über Push-Konstanten zu übergeben, aber das gleiche seltsame Verhalten zu bekommen. – user3067395