Pass:copy; Pass:blit;

This commit is contained in:
bjorn 2022-05-25 23:52:24 -07:00
parent 885e335f8c
commit af0e388e91
5 changed files with 224 additions and 12 deletions

View File

@ -1,8 +1,11 @@
#include "api.h"
#include "graphics/graphics.h"
#include "data/blob.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
static int l_lovrPassGetType(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
@ -263,6 +266,109 @@ static int l_lovrPassClear(lua_State* L) {
return luax_typeerror(L, 2, "Buffer or Texture");
}
static int l_lovrPassCopy(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Blob* blob = luax_totype(L, 2, Blob);
if (blob) {
Buffer* buffer = luax_checktype(L, 3, Buffer);
uint32_t srcOffset = luax_optu32(L, 4, 0);
uint32_t dstOffset = luax_optu32(L, 5, 0);
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t limit = MIN(blob->size - srcOffset, info->length * info->stride - dstOffset);
uint32_t extent = luax_optu32(L, 6, limit);
lovrCheck(extent < blob->size - srcOffset, "Buffer copy range exceeds Blob size");
lovrCheck(extent < info->length * info->stride, "Buffer copy offset exceeds Buffer size");
lovrPassCopyDataToBuffer(pass, (char*) blob->data + srcOffset, buffer, dstOffset, extent);
return 0;
}
Buffer* buffer = luax_totype(L, 2, Buffer);
if (buffer) {
Buffer* dst = luax_checktype(L, 3, Buffer);
uint32_t srcOffset = luax_optu32(L, 4, 0);
uint32_t dstOffset = luax_optu32(L, 5, 0);
const BufferInfo* srcInfo = lovrBufferGetInfo(buffer);
const BufferInfo* dstInfo = lovrBufferGetInfo(dst);
uint32_t limit = MIN(srcInfo->length * srcInfo->stride - srcOffset, dstInfo->length * dstInfo->stride - dstOffset);
uint32_t extent = luax_optu32(L, 6, limit);
lovrPassCopyBufferToBuffer(pass, buffer, dst, srcOffset, dstOffset, extent);
return 0;
}
Image* image = luax_checktype(L, 2, Image);
if (image) {
Texture* texture = luax_checktype(L, 3, Texture);
uint32_t srcOffset[4];
uint32_t dstOffset[4];
uint32_t extent[3];
srcOffset[0] = luax_optu32(L, 4, 0);
srcOffset[1] = luax_optu32(L, 5, 0);
dstOffset[0] = luax_optu32(L, 6, 0);
srcOffset[1] = luax_optu32(L, 7, 0);
extent[0] = luax_optu32(L, 8, ~0u);
extent[1] = luax_optu32(L, 9, ~0u);
srcOffset[2] = luax_optu32(L, 10, 1) - 1;
dstOffset[2] = luax_optu32(L, 11, 1) - 1;
extent[2] = luax_optu32(L, 12, ~0u);
srcOffset[3] = luax_optu32(L, 13, 1) - 1;
dstOffset[3] = luax_optu32(L, 14, 1) - 1;
lovrPassCopyImageToTexture(pass, image, texture, srcOffset, dstOffset, extent);
return 0;
}
Texture* texture = luax_checktype(L, 2, Texture);
if (texture) {
Texture* dst = luax_checktype(L, 3, Texture);
uint32_t srcOffset[4];
uint32_t dstOffset[4];
uint32_t extent[3];
srcOffset[0] = luax_optu32(L, 4, 0);
srcOffset[1] = luax_optu32(L, 5, 0);
dstOffset[0] = luax_optu32(L, 6, 0);
srcOffset[1] = luax_optu32(L, 7, 0);
extent[0] = luax_optu32(L, 8, ~0u);
extent[1] = luax_optu32(L, 9, ~0u);
srcOffset[2] = luax_optu32(L, 10, 1) - 1;
dstOffset[2] = luax_optu32(L, 11, 1) - 1;
extent[2] = luax_optu32(L, 12, ~0u);
srcOffset[3] = luax_optu32(L, 13, 1) - 1;
dstOffset[3] = luax_optu32(L, 14, 1) - 1;
lovrPassCopyTextureToTexture(pass, texture, dst, srcOffset, dstOffset, extent);
return 0;
}
return luax_typeerror(L, 2, "Blob, Buffer, Image, or Texture");
}
static int l_lovrPassBlit(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Texture* src = luax_checktype(L, 2, Texture);
Texture* dst = luax_checktype(L, 3, Texture);
uint32_t srcOffset[4], dstOffset[4], srcExtent[3], dstExtent[3];
srcOffset[0] = luax_optu32(L, 4, 0);
srcOffset[1] = luax_optu32(L, 5, 0);
srcOffset[2] = luax_optu32(L, 6, 0);
dstOffset[0] = luax_optu32(L, 7, 0);
dstOffset[1] = luax_optu32(L, 8, 0);
dstOffset[2] = luax_optu32(L, 9, 0);
srcExtent[0] = luax_optu32(L, 10, ~0u);
srcExtent[1] = luax_optu32(L, 11, ~0u);
srcExtent[2] = luax_optu32(L, 12, ~0u);
dstExtent[0] = luax_optu32(L, 13, ~0u);
dstExtent[1] = luax_optu32(L, 14, ~0u);
dstExtent[2] = luax_optu32(L, 15, ~0u);
srcOffset[3] = luax_optu32(L, 16, 1) - 1;
dstOffset[3] = luax_optu32(L, 17, 1) - 1;
FilterMode filter = luax_checkenum(L, 18, FilterMode, "linear");
lovrPassBlit(pass, src, dst, srcOffset, dstOffset, srcExtent, dstExtent, filter);
return 0;
}
const luaL_Reg lovrPass[] = {
{ "getType", l_lovrPassGetType },
{ "push", l_lovrPassPush },
@ -288,5 +394,7 @@ const luaL_Reg lovrPass[] = {
{ "setWireframe", l_lovrPassSetWireframe },
{ "send", l_lovrPassSend },
{ "clear", l_lovrPassClear },
{ "copy", l_lovrPassCopy },
{ "blit", l_lovrPassBlit },
{ NULL, NULL }
};

View File

@ -488,12 +488,12 @@ void gpu_render_end(gpu_stream* stream);
void gpu_compute_begin(gpu_stream* stream);
void gpu_compute_end(gpu_stream* stream);
void gpu_copy_buffers(gpu_stream* stream, gpu_buffer* src, gpu_buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t size);
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint16_t srcOffset[4], uint16_t dstOffset[4], uint16_t size[3]);
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint16_t dstOffset[4], uint16_t extent[3]);
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint16_t srcOffset[4], uint32_t dstOffset, uint16_t extent[3]);
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t size[3]);
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint32_t dstOffset[4], uint32_t extent[3]);
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint32_t srcOffset[4], uint32_t dstOffset, uint32_t extent[3]);
void gpu_clear_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t size);
void gpu_clear_texture(gpu_stream* stream, gpu_texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint16_t srcOffset[4], uint16_t dstOffset[4], uint16_t srcExtent[3], uint16_t dstExtent[3], gpu_filter filter);
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], gpu_filter filter);
// Entry

View File

@ -1439,7 +1439,7 @@ void gpu_copy_buffers(gpu_stream* stream, gpu_buffer* src, gpu_buffer* dst, uint
});
}
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint16_t srcOffset[4], uint16_t dstOffset[4], uint16_t size[3]) {
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t size[3]) {
vkCmdCopyImage(stream->commands, src->handle, VK_IMAGE_LAYOUT_GENERAL, dst->handle, VK_IMAGE_LAYOUT_GENERAL, 1, &(VkImageCopy) {
.srcSubresource = {
.aspectMask = src->aspect,
@ -1459,7 +1459,7 @@ void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, u
});
}
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint16_t dstOffset[4], uint16_t extent[3]) {
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint32_t dstOffset[4], uint32_t extent[3]) {
VkBufferImageCopy region = {
.bufferOffset = src->offset + srcOffset,
.imageSubresource.aspectMask = dst->aspect,
@ -1473,7 +1473,7 @@ void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* d
vkCmdCopyBufferToImage(stream->commands, src->handle, dst->handle, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
}
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint16_t srcOffset[4], uint32_t dstOffset, uint16_t extent[3]) {
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint32_t srcOffset[4], uint32_t dstOffset, uint32_t extent[3]) {
VkBufferImageCopy region = {
.bufferOffset = dst->offset + dstOffset,
.imageSubresource.aspectMask = src->aspect,
@ -1512,7 +1512,7 @@ void gpu_clear_texture(gpu_stream* stream, gpu_texture* texture, float value[4],
}
}
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint16_t srcOffset[4], uint16_t dstOffset[4], uint16_t srcExtent[3], uint16_t dstExtent[3], gpu_filter filter) {
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], gpu_filter filter) {
VkImageBlit region = {
.srcSubresource = {
.aspectMask = src->aspect,

View File

@ -155,6 +155,7 @@ static gpu_stream* getTransfers(void);
static uint32_t getLayout(gpu_slot* slots, uint32_t count);
static gpu_texture* getAttachment(uint32_t size[2], uint32_t layers, TextureFormat format, bool srgb, uint32_t samples);
static size_t measureTexture(TextureFormat format, uint16_t w, uint16_t h, uint16_t d);
static void checkTextureBounds(const TextureInfo* info, uint32_t offset[4], uint32_t extent[3]);
static uint32_t findShaderSlot(Shader* shader, const char* name, size_t length);
static void checkShaderFeatures(uint32_t* features, uint32_t count);
static void onMessage(void* context, const char* message, bool severe);
@ -556,10 +557,10 @@ Texture* lovrTextureCreate(TextureInfo* info) {
lovrCheck(info->depth == 6 || info->type != TEXTURE_CUBE, "Cubemaps must have a depth of 6");
lovrCheck(info->width == info->height || info->type != TEXTURE_CUBE, "Cubemaps must be square");
lovrCheck(measureTexture(info->format, info->width, info->height, info->depth) < 1 << 30, "Memory for a Texture can not exceed 1GB"); // TODO mip?
lovrCheck(info->samples == 1 || info->samples == 4, "Currently, Texture multisample count must be 1 or 4");
lovrCheck(info->samples == 1 || info->samples == 4, "Texture multisample count must be 1 or 4...for now");
lovrCheck(info->samples == 1 || info->type != TEXTURE_CUBE, "Cubemaps can not be multisampled");
lovrCheck(info->samples == 1 || info->type != TEXTURE_3D, "Volume textures can not be multisampled");
lovrCheck(info->samples == 1 || ~info->usage & TEXTURE_STORAGE, "Currently, Textures with the 'storage' flag can not be multisampled");
lovrCheck(info->samples == 1 || ~info->usage & TEXTURE_STORAGE, "Textures with the 'storage' flag can not be multisampled...for now");
lovrCheck(info->samples == 1 || mipmaps == 1, "Multisampled textures can only have 1 mipmap");
lovrCheck(~info->usage & TEXTURE_SAMPLE || (supports & GPU_FEATURE_SAMPLE), "GPU does not support the 'sample' flag for this format");
lovrCheck(~info->usage & TEXTURE_RENDER || (supports & GPU_FEATURE_RENDER), "GPU does not support the 'render' flag for this format");
@ -1111,7 +1112,7 @@ Pass* lovrGraphicsGetPass(PassInfo* info) {
lovrCheck(main->width <= state.limits.renderSize[0], "Render pass width (%d) exceeds the renderSize limit of this GPU (%d)", main->width, state.limits.renderSize[0]);
lovrCheck(main->height <= state.limits.renderSize[1], "Render pass height (%d) exceeds the renderSize limit of this GPU (%d)", main->height, state.limits.renderSize[1]);
lovrCheck(main->depth <= state.limits.renderSize[2], "Render pass view count (%d) exceeds the renderSize limit of this GPU (%d)", main->depth, state.limits.renderSize[2]);
lovrCheck(canvas->samples == 1 || canvas->samples == 4, "Currently, render pass sample count must be 1 or 4");
lovrCheck(canvas->samples == 1 || canvas->samples == 4, "Render pass sample count must be 1 or 4...for now");
uint32_t colorTextureCount = 0;
for (uint32_t i = 0; i < COUNTOF(canvas->textures) && canvas->textures[i]; i++, colorTextureCount++) {
@ -1136,7 +1137,7 @@ Pass* lovrGraphicsGetPass(PassInfo* info) {
lovrCheck(texture->width == main->width, "Render pass texture sizes must match");
lovrCheck(texture->height == main->height, "Render pass texture sizes must match");
lovrCheck(texture->depth == main->depth, "Render pass texture sizes must match");
lovrCheck(texture->samples == main->samples, "Currently, depth buffer sample count must match the main render pass sample count");
lovrCheck(texture->samples == main->samples, "Depth buffer sample count must match the main render pass sample count...for now");
}
}
@ -1535,11 +1536,98 @@ void lovrPassClearBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t e
void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount) {
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(!texture->info.parent, "Texture views can not be cleared");
lovrCheck(texture->info.usage & TEXTURE_TRANSFER, "Texture must be created with 'transfer' usage to clear it");
lovrCheck(texture->info.type == TEXTURE_3D || layer + layerCount <= texture->info.depth, "Texture clear range exceeds texture layer count");
lovrCheck(level + levelCount <= texture->info.mipmaps, "Texture clear range exceeds texture mipmap count");
gpu_clear_texture(pass->stream, texture->gpu, value, layer, layerCount, level, levelCount);
}
void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t offset, uint32_t extent) {
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(!lovrBufferIsTemporary(buffer), "Temporary buffers can not be copied to, use Buffer:setData");
lovrCheck(offset + extent <= buffer->size, "Buffer copy range goes past the end of the Buffer");
gpu_buffer* scratchpad = tempAlloc(gpu_sizeof_buffer());
void* pointer = gpu_map(scratchpad, extent, 4, GPU_MAP_WRITE);
gpu_copy_buffers(pass->stream, scratchpad, buffer->gpu, 0, offset, extent);
memcpy(pointer, data, extent);
}
void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent) {
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(!lovrBufferIsTemporary(dst), "Temporary buffers can not be copied to");
lovrCheck(srcOffset + extent <= src->size, "Buffer copy range goes past the end of the source Buffer");
lovrCheck(dstOffset + extent <= dst->size, "Buffer copy range goes past the end of the destination Buffer");
gpu_copy_buffers(pass->stream, src->gpu, dst->gpu, srcOffset, dstOffset, extent);
}
void lovrPassCopyImageToTexture(Pass* pass, Image* image, Texture* texture, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[4]) {
if (extent[0] == ~0u) extent[0] = MIN(texture->info.width - dstOffset[0], lovrImageGetWidth(image, srcOffset[3]) - srcOffset[0]);
if (extent[1] == ~0u) extent[1] = MIN(texture->info.height - dstOffset[1], lovrImageGetHeight(image, srcOffset[3]) - srcOffset[1]);
if (extent[2] == ~0u) extent[2] = MIN(texture->info.depth - dstOffset[2], lovrImageGetLayerCount(image) - srcOffset[2]);
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(texture->info.usage & TEXTURE_TRANSFER, "Texture must be created with the 'transfer' usage to copy to it");
lovrCheck(!texture->info.parent, "Texture views can not be written to");
lovrCheck(texture->info.samples == 1, "Multisampled Textures can not be written to");
lovrCheck(lovrImageGetFormat(image) == texture->info.format, "Image and Texture formats must match");
lovrCheck(srcOffset[0] + extent[0] <= lovrImageGetWidth(image, srcOffset[3]), "Image copy region exceeds its %s", "width");
lovrCheck(srcOffset[1] + extent[1] <= lovrImageGetHeight(image, srcOffset[3]), "Image copy region exceeds its %s", "height");
lovrCheck(srcOffset[2] + extent[2] <= lovrImageGetLayerCount(image), "Image copy region exceeds its %s", "layer count");
lovrCheck(srcOffset[3] < lovrImageGetLevelCount(image), "Image copy region exceeds its %s", "mipmap count");
checkTextureBounds(&texture->info, dstOffset, extent);
size_t rowSize = measureTexture(texture->info.format, extent[0], 1, 1);
size_t totalSize = measureTexture(texture->info.format, extent[0], extent[1], 1) * extent[2];
size_t layerOffset = measureTexture(texture->info.format, extent[0], srcOffset[1], 1);
layerOffset += measureTexture(texture->info.format, srcOffset[0], 1, 1);
size_t pitch = measureTexture(texture->info.format, lovrImageGetWidth(image, srcOffset[3]), 1, 1);
gpu_buffer* buffer = tempAlloc(gpu_sizeof_buffer());
char* dst = gpu_map(buffer, totalSize, 64, GPU_MAP_WRITE);
for (uint32_t z = 0; z < extent[2]; z++) {
const char* src = (char*) lovrImageGetLayerData(image, srcOffset[3], z) + layerOffset;
for (uint32_t y = 0; y < extent[1]; y++) {
memcpy(dst, src, rowSize);
dst += rowSize;
src += pitch;
}
}
gpu_copy_buffer_texture(pass->stream, buffer, texture->gpu, 0, dstOffset, extent);
}
void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]) {
if (extent[0] == ~0u) extent[0] = MIN(src->info.width - srcOffset[0], dst->info.width - dstOffset[0]);
if (extent[1] == ~0u) extent[1] = MIN(src->info.height - srcOffset[1], dst->info.height - dstOffset[0]);
if (extent[2] == ~0u) extent[2] = MIN(src->info.depth - srcOffset[2], dst->info.depth - dstOffset[0]);
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(src->info.usage & TEXTURE_TRANSFER, "Texture must be created with the 'transfer' usage to copy %s it", "from");
lovrCheck(dst->info.usage & TEXTURE_TRANSFER, "Texture must be created with the 'transfer' usage to copy %s it", "to");
lovrCheck(!src->info.parent && !dst->info.parent, "Can not copy texture views");
lovrCheck(src->info.format == dst->info.format, "Copying between Textures requires them to have the same format");
lovrCheck(src->info.samples == dst->info.samples, "Texture sample counts must match to copy between them");
checkTextureBounds(&src->info, srcOffset, extent);
checkTextureBounds(&dst->info, dstOffset, extent);
gpu_copy_textures(pass->stream, src->gpu, dst->gpu, srcOffset, dstOffset, extent);
}
void lovrPassBlit(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], FilterMode filter) {
if (srcExtent[0] == ~0u) srcExtent[0] = src->info.width - srcOffset[0];
if (srcExtent[1] == ~0u) srcExtent[1] = src->info.height - srcOffset[1];
if (srcExtent[2] == ~0u) srcExtent[2] = src->info.depth - srcOffset[2];
if (dstExtent[0] == ~0u) dstExtent[0] = dst->info.width - dstOffset[0];
if (dstExtent[1] == ~0u) dstExtent[1] = dst->info.height - dstOffset[1];
if (dstExtent[2] == ~0u) dstExtent[2] = dst->info.depth - dstOffset[2];
lovrCheck(pass->info.type == PASS_TRANSFER, "This function can only be called on a transfer pass");
lovrCheck(!src->info.parent && !dst->info.parent, "Can not blit Texture views");
lovrCheck(src->info.samples == 1 && dst->info.samples == 1, "Multisampled textures can not be used for blits");
lovrCheck(src->info.usage & TEXTURE_TRANSFER, "Texture must have the 'copy' flag to blit %s it", "from");
lovrCheck(dst->info.usage & TEXTURE_TRANSFER, "Texture must have the 'copy' flag to blit %s it", "to");
lovrCheck(state.features.formats[src->info.format] & GPU_FEATURE_BLIT_SRC, "This GPU does not support blitting from the source texture's format");
lovrCheck(state.features.formats[dst->info.format] & GPU_FEATURE_BLIT_DST, "This GPU does not support blitting to the destination texture's format");
lovrCheck(src->info.format == dst->info.format, "Texture formats must match to blit between them");
// FIXME if src or dst is 3D you can only blit 1 layer or something!
checkTextureBounds(&src->info, srcOffset, srcExtent);
checkTextureBounds(&dst->info, dstOffset, dstExtent);
gpu_blit(pass->stream, src->gpu, dst->gpu, srcOffset, dstOffset, srcExtent, dstExtent, (gpu_filter) filter);
}
// Helpers
static void* tempAlloc(size_t size) {
@ -1703,6 +1791,17 @@ static size_t measureTexture(TextureFormat format, uint16_t w, uint16_t h, uint1
}
}
// Errors if a 3D texture region exceeds the texture's bounds
static void checkTextureBounds(const TextureInfo* info, uint32_t offset[4], uint32_t extent[3]) {
uint32_t maxWidth = MAX(info->width >> offset[3], 1);
uint32_t maxHeight = MAX(info->height >> offset[3], 1);
uint32_t maxDepth = info->type == TEXTURE_3D ? MAX(info->depth >> offset[3], 1) : info->depth;
lovrCheck(offset[0] + extent[0] <= maxWidth, "Texture x range [%d,%d] exceeds width (%d)", offset[0], offset[0] + extent[0], maxWidth);
lovrCheck(offset[1] + extent[1] <= maxHeight, "Texture y range [%d,%d] exceeds height (%d)", offset[1], offset[1] + extent[1], maxHeight);
lovrCheck(offset[2] + extent[2] <= maxDepth, "Texture z range [%d,%d] exceeds depth (%d)", offset[2], offset[2] + extent[2], maxDepth);
lovrCheck(offset[3] < info->mipmaps, "Texture mipmap %d exceeds its mipmap count (%d)", offset[3] + 1, info->mipmaps);
}
uint32_t findShaderSlot(Shader* shader, const char* name, size_t length) {
uint32_t hash = (uint32_t) hash64(name, length);
for (uint32_t i = 0; i < shader->resourceCount; i++) {

View File

@ -374,3 +374,8 @@ void lovrPassSendTexture(Pass* pass, const char* name, size_t length, uint32_t s
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t slot, Sampler* sampler);
void lovrPassClearBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void lovrPassCopyDataToBuffer(Pass* pass, void* data, Buffer* buffer, uint32_t offset, uint32_t size);
void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent);
void lovrPassCopyImageToTexture(Pass* pass, struct Image* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassBlit(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], FilterMode filter);