mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
436 lines
20 KiB
C++
436 lines
20 KiB
C++
#include "vulkan_bridge.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <sys/mman.h>
|
|
|
|
namespace raven {
|
|
|
|
// Names are kept in byte order because the guest provider binary searches its export table.
|
|
static constexpr std::array<std::string_view, 25> names{
|
|
"vkAllocateCommandBuffers", "vkAllocateMemory", "vkBeginCommandBuffer", "vkBindBufferMemory", "vkCmdCopyBuffer",
|
|
"vkCreateBuffer", "vkCreateCommandPool", "vkCreateFence", "vkDestroyBuffer", "vkDestroyCommandPool", "vkDestroyFence",
|
|
"vkEndCommandBuffer", "vkFlushMappedMemoryRanges", "vkFreeCommandBuffers", "vkFreeMemory", "vkGetBufferMemoryRequirements",
|
|
"vkInvalidateMappedMemoryRanges", "vkMapMemory", "vkQueueSubmit", "vkQueueWaitIdle", "vkResetFences", "vkUnmapMemory",
|
|
"vkWaitForFences", "vkGetDeviceMemoryCommitment", "vkResetCommandPool"
|
|
};
|
|
|
|
std::span<const std::string_view> vulkan_resource_names()
|
|
{
|
|
static const auto sorted = [] {
|
|
std::array<std::string_view, 25> copy = names;
|
|
std::sort(copy.begin(), copy.end());
|
|
return copy;
|
|
}();
|
|
return sorted;
|
|
}
|
|
|
|
void VulkanBridge::check_writable(uint64_t address, size_t size) const
|
|
{
|
|
process.write_memory(address, process.read_memory(address, size));
|
|
}
|
|
|
|
void VulkanBridge::shadow_to_device(uint64_t device)
|
|
{
|
|
for (auto &[token, memory] : memories) {
|
|
if (memory.device != device || !memory.host || !memory.shadow)
|
|
continue;
|
|
auto current = process.read_memory(memory.shadow, size_t(memory.mapped));
|
|
auto *host = static_cast<uint8_t *>(memory.host);
|
|
for (size_t index = 0; index < current.size(); ++index)
|
|
if (current[index] != memory.snapshot[index]) {
|
|
host[index] = current[index];
|
|
memory.snapshot[index] = current[index];
|
|
}
|
|
}
|
|
}
|
|
|
|
void VulkanBridge::shadow_from_device(uint64_t device)
|
|
{
|
|
for (auto &[token, memory] : memories) {
|
|
if (memory.device != device || !memory.host || !memory.shadow)
|
|
continue;
|
|
const auto *host = static_cast<const uint8_t *>(memory.host);
|
|
std::vector<uint8_t> current = process.read_memory(memory.shadow, size_t(memory.mapped));
|
|
bool changed = false;
|
|
for (size_t index = 0; index < current.size(); ++index)
|
|
if (host[index] != memory.snapshot[index]) {
|
|
current[index] = host[index];
|
|
memory.snapshot[index] = host[index];
|
|
changed = true;
|
|
}
|
|
if (changed)
|
|
process.write_memory(memory.shadow, current);
|
|
}
|
|
}
|
|
|
|
bool VulkanBridge::dispatch_resource(std::string_view name)
|
|
{
|
|
if (std::find(names.begin(), names.end(), name) == names.end())
|
|
return false;
|
|
static const std::map<std::string_view, size_t, std::less<>> words{
|
|
{"vkAllocateCommandBuffers", 3}, {"vkAllocateMemory", 4}, {"vkBeginCommandBuffer", 2}, {"vkBindBufferMemory", 4},
|
|
{"vkCmdCopyBuffer", 5}, {"vkCreateBuffer", 4}, {"vkCreateCommandPool", 4}, {"vkCreateFence", 4}, {"vkDestroyBuffer", 3},
|
|
{"vkDestroyCommandPool", 3}, {"vkDestroyFence", 3}, {"vkEndCommandBuffer", 1}, {"vkFlushMappedMemoryRanges", 3},
|
|
{"vkFreeCommandBuffers", 4}, {"vkFreeMemory", 3}, {"vkGetBufferMemoryRequirements", 3}, {"vkInvalidateMappedMemoryRanges", 3},
|
|
{"vkMapMemory", 6}, {"vkQueueSubmit", 4}, {"vkQueueWaitIdle", 1}, {"vkResetFences", 3}, {"vkUnmapMemory", 2}, {"vkWaitForFences", 5},
|
|
{"vkGetDeviceMemoryCommitment", 3}, {"vkResetCommandPool", 3}};
|
|
auto a = process.api_arguments(words.at(name));
|
|
// A 32-bit argument leaves the upper half of its register or stack slot undefined, so those are narrowed before use.
|
|
static const std::map<std::string_view, std::vector<size_t>, std::less<>> narrow{
|
|
{"vkAllocateMemory", {}}, {"vkMapMemory", {4}}, {"vkFlushMappedMemoryRanges", {1}}, {"vkInvalidateMappedMemoryRanges", {1}},
|
|
{"vkFreeCommandBuffers", {2}}, {"vkCmdCopyBuffer", {3}}, {"vkQueueSubmit", {1}}, {"vkResetFences", {1}}, {"vkWaitForFences", {1, 3}},
|
|
{"vkResetCommandPool", {2}}};
|
|
if (const auto found = narrow.find(name); found != narrow.end())
|
|
for (const auto index : found->second)
|
|
a[index] = uint32_t(a[index]);
|
|
const auto device_of = [&](uint64_t token) -> Device & {
|
|
const auto found = devices.find(token);
|
|
if (found == devices.end())
|
|
throw std::runtime_error("invalid Vulkan device handle");
|
|
return found->second;
|
|
};
|
|
const auto no_allocator = [&](uint64_t pointer) {
|
|
if (pointer)
|
|
throw std::runtime_error("Vulkan guest allocation callbacks are not implemented");
|
|
};
|
|
const auto memory_of = [&](uint64_t token, uint64_t device) -> Memory & {
|
|
const auto found = memories.find(token);
|
|
if (found == memories.end() || found->second.device != device)
|
|
throw std::runtime_error("invalid Vulkan memory handle");
|
|
return found->second;
|
|
};
|
|
const auto buffer_of = [&](uint64_t token, uint64_t device) -> Buffer & {
|
|
const auto found = buffers.find(token);
|
|
if (found == buffers.end() || found->second.device != device)
|
|
throw std::runtime_error("invalid Vulkan buffer handle");
|
|
return found->second;
|
|
};
|
|
const auto pool_of = [&](uint64_t token, uint64_t device) -> Pool & {
|
|
const auto found = pools.find(token);
|
|
if (found == pools.end() || found->second.device != device)
|
|
throw std::runtime_error("invalid Vulkan command pool handle");
|
|
return found->second;
|
|
};
|
|
const auto command_of = [&](uint64_t token) -> CommandBuffer & {
|
|
const auto found = command_buffers.find(token);
|
|
if (found == command_buffers.end())
|
|
throw std::runtime_error("invalid Vulkan command buffer handle");
|
|
return found->second;
|
|
};
|
|
const auto fence_of = [&](uint64_t token, uint64_t device) -> Fence & {
|
|
const auto found = fences.find(token);
|
|
if (found == fences.end() || found->second.device != device)
|
|
throw std::runtime_error("invalid Vulkan fence handle");
|
|
return found->second;
|
|
};
|
|
uint64_t result = 0;
|
|
if (name == "vkCreateBuffer") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
auto info = load<VkBufferCreateInfo>(a[1]);
|
|
if (info.sType != VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO || info.pNext || info.flags || info.sharingMode != VK_SHARING_MODE_EXCLUSIVE ||
|
|
info.queueFamilyIndexCount || info.pQueueFamilyIndices || !info.size || info.size > (1ULL << 30))
|
|
throw std::runtime_error("unsupported Vulkan buffer create info");
|
|
check_writable(a[3], 8);
|
|
VkBuffer buffer = VK_NULL_HANDLE;
|
|
const auto status = vkCreateBuffer(device.handle, &info, nullptr, &buffer);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS) {
|
|
const auto token = next_handle++;
|
|
buffers.emplace(token, Buffer{buffer, a[0]});
|
|
store(a[3], token);
|
|
}
|
|
} else if (name == "vkDestroyBuffer") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
if (a[1]) {
|
|
const auto &buffer = buffer_of(a[1], a[0]);
|
|
vkDestroyBuffer(device.handle, buffer.handle, nullptr);
|
|
buffers.erase(a[1]);
|
|
}
|
|
} else if (name == "vkGetBufferMemoryRequirements") {
|
|
auto &device = device_of(a[0]);
|
|
const auto &buffer = buffer_of(a[1], a[0]);
|
|
check_writable(a[2], sizeof(VkMemoryRequirements));
|
|
VkMemoryRequirements requirements{};
|
|
vkGetBufferMemoryRequirements(device.handle, buffer.handle, &requirements);
|
|
store(a[2], requirements);
|
|
} else if (name == "vkAllocateMemory") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
auto info = load<VkMemoryAllocateInfo>(a[1]);
|
|
if (info.sType != VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO || info.pNext || !info.allocationSize || info.allocationSize > (256ULL << 20))
|
|
throw std::runtime_error("unsupported Vulkan memory allocate info");
|
|
check_writable(a[3], 8);
|
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
|
const auto status = vkAllocateMemory(device.handle, &info, nullptr, &memory);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS) {
|
|
const auto token = next_handle++;
|
|
memories.emplace(token, Memory{memory, a[0], info.allocationSize});
|
|
store(a[3], token);
|
|
}
|
|
} else if (name == "vkFreeMemory") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
if (a[1]) {
|
|
auto &memory = memory_of(a[1], a[0]);
|
|
if (memory.host) {
|
|
vkUnmapMemory(device.handle, memory.handle);
|
|
if (memory.shadow)
|
|
process.unmap_memory(memory.shadow);
|
|
}
|
|
vkFreeMemory(device.handle, memory.handle, nullptr);
|
|
memories.erase(a[1]);
|
|
}
|
|
} else if (name == "vkBindBufferMemory") {
|
|
auto &device = device_of(a[0]);
|
|
const auto &buffer = buffer_of(a[1], a[0]);
|
|
const auto &memory = memory_of(a[2], a[0]);
|
|
result = static_cast<uint32_t>(vkBindBufferMemory(device.handle, buffer.handle, memory.handle, a[3]));
|
|
} else if (name == "vkMapMemory") {
|
|
auto &device = device_of(a[0]);
|
|
auto &memory = memory_of(a[1], a[0]);
|
|
const uint64_t offset = a[2], size = a[3] == VK_WHOLE_SIZE ? memory.size - std::min<uint64_t>(offset, memory.size) : a[3];
|
|
if (a[4] || memory.host || offset >= memory.size || !size || size > memory.size - offset || size > (256ULL << 20))
|
|
throw std::runtime_error("unsupported Vulkan memory mapping (offset " + std::to_string(offset) + ", size " + std::to_string(size) +
|
|
", memory " + std::to_string(memory.size) + (memory.host ? ", already mapped" : "") + ", flags " + std::to_string(a[4]) + ")");
|
|
check_writable(a[5], 8);
|
|
void *host = nullptr;
|
|
const auto status = vkMapMemory(device.handle, memory.handle, offset, size, 0, &host);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS) {
|
|
const auto rounded = (size_t(size) + 4095) & ~size_t(4095);
|
|
const auto shadow = process.map_memory(rounded, PROT_READ | PROT_WRITE);
|
|
memory.snapshot.assign(static_cast<const uint8_t *>(host), static_cast<const uint8_t *>(host) + size);
|
|
process.write_memory(shadow, memory.snapshot);
|
|
memory.host = host;
|
|
memory.shadow = shadow;
|
|
memory.offset = offset;
|
|
memory.mapped = size;
|
|
store(a[5], shadow);
|
|
}
|
|
} else if (name == "vkUnmapMemory") {
|
|
auto &device = device_of(a[0]);
|
|
auto &memory = memory_of(a[1], a[0]);
|
|
if (!memory.host)
|
|
throw std::runtime_error("Vulkan memory is not mapped");
|
|
shadow_to_device(a[0]);
|
|
vkUnmapMemory(device.handle, memory.handle);
|
|
process.unmap_memory(memory.shadow);
|
|
memory.host = nullptr;
|
|
memory.shadow = 0;
|
|
memory.snapshot.clear();
|
|
} else if (name == "vkFlushMappedMemoryRanges" || name == "vkInvalidateMappedMemoryRanges") {
|
|
device_of(a[0]);
|
|
if (a[1] > 64)
|
|
throw std::runtime_error("too many Vulkan mapped memory ranges");
|
|
for (uint64_t index = 0; index < a[1]; ++index) {
|
|
const auto range = load<VkMappedMemoryRange>(a[2] + index * sizeof(VkMappedMemoryRange));
|
|
if (range.sType != VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE || range.pNext)
|
|
throw std::runtime_error("unsupported Vulkan mapped memory range");
|
|
if (!memory_of(reinterpret_cast<uint64_t>(range.memory), a[0]).host)
|
|
throw std::runtime_error("Vulkan memory is not mapped");
|
|
}
|
|
if (name == "vkFlushMappedMemoryRanges")
|
|
shadow_to_device(a[0]);
|
|
else
|
|
shadow_from_device(a[0]);
|
|
} else if (name == "vkCreateCommandPool") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
auto info = load<VkCommandPoolCreateInfo>(a[1]);
|
|
if (info.sType != VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO || info.pNext || (info.flags & ~uint32_t(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)))
|
|
throw std::runtime_error("unsupported Vulkan command pool create info");
|
|
check_writable(a[3], 8);
|
|
VkCommandPool pool = VK_NULL_HANDLE;
|
|
const auto status = vkCreateCommandPool(device.handle, &info, nullptr, &pool);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS) {
|
|
const auto token = next_handle++;
|
|
pools.emplace(token, Pool{pool, a[0]});
|
|
store(a[3], token);
|
|
}
|
|
} else if (name == "vkDestroyCommandPool") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
if (a[1]) {
|
|
const auto &pool = pool_of(a[1], a[0]);
|
|
vkDestroyCommandPool(device.handle, pool.handle, nullptr);
|
|
std::erase_if(command_buffers, [&](const auto &item) { return item.second.pool == a[1]; });
|
|
pools.erase(a[1]);
|
|
}
|
|
} else if (name == "vkResetCommandPool") {
|
|
auto &device = device_of(a[0]);
|
|
const auto &pool = pool_of(a[1], a[0]);
|
|
result = static_cast<uint32_t>(vkResetCommandPool(device.handle, pool.handle, VkCommandPoolResetFlags(uint32_t(a[2]))));
|
|
} else if (name == "vkAllocateCommandBuffers") {
|
|
auto &device = device_of(a[0]);
|
|
auto info = load<VkCommandBufferAllocateInfo>(a[1]);
|
|
if (info.sType != VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO || info.pNext || !info.commandBufferCount || info.commandBufferCount > 64)
|
|
throw std::runtime_error("unsupported Vulkan command buffer allocate info");
|
|
const auto pool_token = reinterpret_cast<uint64_t>(info.commandPool);
|
|
const auto &pool = pool_of(pool_token, a[0]);
|
|
info.commandPool = pool.handle;
|
|
check_writable(a[2], size_t(info.commandBufferCount) * 8);
|
|
std::vector<VkCommandBuffer> created(info.commandBufferCount);
|
|
const auto status = vkAllocateCommandBuffers(device.handle, &info, created.data());
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS)
|
|
for (size_t index = 0; index < created.size(); ++index) {
|
|
const auto token = next_handle++;
|
|
command_buffers.emplace(token, CommandBuffer{created[index], a[0], pool_token});
|
|
store(a[2] + index * 8, token);
|
|
}
|
|
} else if (name == "vkFreeCommandBuffers") {
|
|
auto &device = device_of(a[0]);
|
|
const auto &pool = pool_of(a[1], a[0]);
|
|
if (a[2] > 64)
|
|
throw std::runtime_error("too many Vulkan command buffers");
|
|
std::vector<VkCommandBuffer> handles;
|
|
std::vector<uint64_t> tokens;
|
|
for (uint64_t index = 0; index < a[2]; ++index) {
|
|
const auto token = load<uint64_t>(a[3] + index * 8);
|
|
if (!token)
|
|
continue;
|
|
const auto &buffer = command_of(token);
|
|
if (buffer.pool != a[1])
|
|
throw std::runtime_error("Vulkan command buffer belongs to another pool");
|
|
handles.push_back(buffer.handle);
|
|
tokens.push_back(token);
|
|
}
|
|
if (!handles.empty())
|
|
vkFreeCommandBuffers(device.handle, pool.handle, uint32_t(handles.size()), handles.data());
|
|
for (const auto token : tokens)
|
|
command_buffers.erase(token);
|
|
} else if (name == "vkBeginCommandBuffer") {
|
|
auto &buffer = command_of(a[0]);
|
|
auto info = load<VkCommandBufferBeginInfo>(a[1]);
|
|
if (info.sType != VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO || info.pNext || info.pInheritanceInfo)
|
|
throw std::runtime_error("unsupported Vulkan command buffer begin info");
|
|
result = static_cast<uint32_t>(vkBeginCommandBuffer(buffer.handle, &info));
|
|
} else if (name == "vkEndCommandBuffer") {
|
|
result = static_cast<uint32_t>(vkEndCommandBuffer(command_of(a[0]).handle));
|
|
} else if (name == "vkCmdCopyBuffer") {
|
|
auto &command = command_of(a[0]);
|
|
const auto &source = buffer_of(a[1], command.device);
|
|
const auto &target = buffer_of(a[2], command.device);
|
|
if (!a[3] || a[3] > 64)
|
|
throw std::runtime_error("unsupported Vulkan buffer copy region count");
|
|
std::vector<VkBufferCopy> regions(a[3]);
|
|
for (size_t index = 0; index < regions.size(); ++index)
|
|
regions[index] = load<VkBufferCopy>(a[4] + index * sizeof(VkBufferCopy));
|
|
vkCmdCopyBuffer(command.handle, source.handle, target.handle, uint32_t(regions.size()), regions.data());
|
|
} else if (name == "vkCreateFence") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
auto info = load<VkFenceCreateInfo>(a[1]);
|
|
if (info.sType != VK_STRUCTURE_TYPE_FENCE_CREATE_INFO || info.pNext || (info.flags & ~uint32_t(VK_FENCE_CREATE_SIGNALED_BIT)))
|
|
throw std::runtime_error("unsupported Vulkan fence create info");
|
|
check_writable(a[3], 8);
|
|
VkFence fence = VK_NULL_HANDLE;
|
|
const auto status = vkCreateFence(device.handle, &info, nullptr, &fence);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS) {
|
|
const auto token = next_handle++;
|
|
fences.emplace(token, Fence{fence, a[0]});
|
|
store(a[3], token);
|
|
}
|
|
} else if (name == "vkDestroyFence") {
|
|
auto &device = device_of(a[0]);
|
|
no_allocator(a[2]);
|
|
if (a[1]) {
|
|
const auto &fence = fence_of(a[1], a[0]);
|
|
vkDestroyFence(device.handle, fence.handle, nullptr);
|
|
fences.erase(a[1]);
|
|
}
|
|
} else if (name == "vkResetFences" || name == "vkWaitForFences") {
|
|
auto &device = device_of(a[0]);
|
|
if (!a[1] || a[1] > 64)
|
|
throw std::runtime_error("unsupported Vulkan fence count");
|
|
std::vector<VkFence> handles;
|
|
for (uint64_t index = 0; index < a[1]; ++index)
|
|
handles.push_back(fence_of(load<uint64_t>(a[2] + index * 8), a[0]).handle);
|
|
if (name == "vkResetFences") {
|
|
result = static_cast<uint32_t>(vkResetFences(device.handle, uint32_t(handles.size()), handles.data()));
|
|
} else {
|
|
const auto status = vkWaitForFences(device.handle, uint32_t(handles.size()), handles.data(), a[3] & 1 ? VK_TRUE : VK_FALSE, a[4]);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS)
|
|
shadow_from_device(a[0]);
|
|
}
|
|
} else if (name == "vkQueueSubmit") {
|
|
const auto found = queues.find(a[0]);
|
|
if (found == queues.end())
|
|
throw std::runtime_error("invalid Vulkan queue handle");
|
|
const auto device_token = found->second.device;
|
|
if (a[1] > 16)
|
|
throw std::runtime_error("too many Vulkan submissions");
|
|
std::vector<VkSubmitInfo> submits(a[1]);
|
|
std::vector<std::vector<VkCommandBuffer>> lists(a[1]);
|
|
std::vector<std::vector<VkSemaphore>> waits(a[1]), signals(a[1]);
|
|
std::vector<std::vector<VkPipelineStageFlags>> stages(a[1]);
|
|
const auto semaphore_list = [&](uint64_t address, uint32_t count, std::vector<VkSemaphore> &out) {
|
|
if (count > 8 || (count && !address))
|
|
throw std::runtime_error("unsupported Vulkan semaphore count");
|
|
for (uint32_t item = 0; item < count; ++item) {
|
|
const auto found_semaphore = objects.find(load<uint64_t>(address + uint64_t(item) * 8));
|
|
if (found_semaphore == objects.end() || found_semaphore->second.kind != Kind::semaphore || found_semaphore->second.device != device_token)
|
|
throw std::runtime_error("invalid Vulkan semaphore handle");
|
|
out.push_back(reinterpret_cast<VkSemaphore>(found_semaphore->second.handle));
|
|
}
|
|
};
|
|
for (size_t index = 0; index < submits.size(); ++index) {
|
|
submits[index] = load<VkSubmitInfo>(a[2] + index * sizeof(VkSubmitInfo));
|
|
auto &info = submits[index];
|
|
if (info.sType != VK_STRUCTURE_TYPE_SUBMIT_INFO || info.pNext || info.commandBufferCount > 64)
|
|
throw std::runtime_error("unsupported Vulkan submit info");
|
|
for (uint32_t buffer = 0; buffer < info.commandBufferCount; ++buffer) {
|
|
const auto &command = command_of(load<uint64_t>(reinterpret_cast<uint64_t>(info.pCommandBuffers) + uint64_t(buffer) * 8));
|
|
if (command.device != device_token)
|
|
throw std::runtime_error("Vulkan command buffer belongs to another device");
|
|
lists[index].push_back(command.handle);
|
|
}
|
|
semaphore_list(reinterpret_cast<uint64_t>(info.pWaitSemaphores), info.waitSemaphoreCount, waits[index]);
|
|
semaphore_list(reinterpret_cast<uint64_t>(info.pSignalSemaphores), info.signalSemaphoreCount, signals[index]);
|
|
if (info.waitSemaphoreCount) {
|
|
if (!info.pWaitDstStageMask)
|
|
throw std::runtime_error("Vulkan wait semaphores need stage masks");
|
|
for (uint32_t item = 0; item < info.waitSemaphoreCount; ++item)
|
|
stages[index].push_back(load<uint32_t>(reinterpret_cast<uint64_t>(info.pWaitDstStageMask) + uint64_t(item) * 4));
|
|
}
|
|
info.pWaitSemaphores = waits[index].data();
|
|
info.pWaitDstStageMask = stages[index].data();
|
|
info.pSignalSemaphores = signals[index].data();
|
|
info.pCommandBuffers = lists[index].data();
|
|
}
|
|
VkFence fence = VK_NULL_HANDLE;
|
|
if (a[3])
|
|
fence = fence_of(a[3], device_token).handle;
|
|
shadow_to_device(device_token);
|
|
result = static_cast<uint32_t>(vkQueueSubmit(found->second.handle, uint32_t(submits.size()), submits.data(), fence));
|
|
} else if (name == "vkQueueWaitIdle") {
|
|
const auto found = queues.find(a[0]);
|
|
if (found == queues.end())
|
|
throw std::runtime_error("invalid Vulkan queue handle");
|
|
const auto status = vkQueueWaitIdle(found->second.handle);
|
|
result = static_cast<uint32_t>(status);
|
|
if (status == VK_SUCCESS)
|
|
shadow_from_device(found->second.device);
|
|
} else if (name == "vkGetDeviceMemoryCommitment") {
|
|
auto &device = device_of(a[0]);
|
|
const auto &memory = memory_of(a[1], a[0]);
|
|
check_writable(a[2], 8);
|
|
VkDeviceSize committed = 0;
|
|
vkGetDeviceMemoryCommitment(device.handle, memory.handle, &committed);
|
|
store(a[2], committed);
|
|
}
|
|
process.return_api(result);
|
|
return true;
|
|
}
|
|
|
|
}
|