mirror of https://github.com/bjornbytes/lovr.git
Sketch out Texture uploads;
This commit is contained in:
parent
b7aa3f29a4
commit
ebe77e5924
|
@ -5,6 +5,7 @@
|
|||
#include "util.h"
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
StringEntry lovrBufferLayout[] = {
|
||||
|
@ -444,7 +445,6 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
int index = 1;
|
||||
Image* stack[6];
|
||||
Image** images = stack;
|
||||
uint32_t imageCount = 0;
|
||||
|
||||
if (lua_isnumber(L, 1)) {
|
||||
info.width = luax_checku32(L, index++);
|
||||
|
@ -454,14 +454,14 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
info.type = TEXTURE_ARRAY;
|
||||
}
|
||||
} else if (lua_istable(L, 1)) {
|
||||
imageCount = luax_len(L, index++);
|
||||
images = imageCount > COUNTOF(stack) ? malloc(imageCount * sizeof(Image*)) : stack;
|
||||
info.imageCount = luax_len(L, index++);
|
||||
images = info.imageCount > COUNTOF(stack) ? malloc(info.imageCount * sizeof(Image*)) : stack;
|
||||
lovrAssert(images, "Out of memory");
|
||||
info.type = TEXTURE_ARRAY;
|
||||
info.depth = imageCount;
|
||||
info.depth = info.imageCount;
|
||||
|
||||
if (imageCount == 0) {
|
||||
imageCount = 6;
|
||||
if (info.imageCount == 0) {
|
||||
info.imageCount = 6;
|
||||
info.type = TEXTURE_CUBE;
|
||||
const char* faces[6] = { "right", "left", "top", "bottom", "back", "front" };
|
||||
const char* altFaces[6] = { "px", "nx", "py", "ny", "pz", "nz" };
|
||||
|
@ -477,8 +477,10 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
images[i] = luax_checkimage(L, -1);
|
||||
}
|
||||
}
|
||||
|
||||
lovrCheck(lovrImageGetLayerCount(images[0]) == 1, "When a list of images is provided, each must have a single layer");
|
||||
} else {
|
||||
imageCount = 1;
|
||||
info.imageCount = 1;
|
||||
images[0] = luax_checkimage(L, index++);
|
||||
info.depth = lovrImageGetLayerCount(images[0]);
|
||||
if (lovrImageIsCube(images[0])) {
|
||||
|
@ -488,7 +490,8 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
}
|
||||
}
|
||||
|
||||
if (imageCount > 0) {
|
||||
if (info.imageCount > 0) {
|
||||
info.images = images;
|
||||
Image* image = images[0];
|
||||
uint32_t layers = lovrImageGetLayerCount(image);
|
||||
uint32_t levels = lovrImageGetLevelCount(image);
|
||||
|
@ -497,11 +500,12 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
info.height = lovrImageGetHeight(image);
|
||||
info.mipmaps = levels == 1 ? ~0u : levels;
|
||||
info.srgb = lovrImageIsSRGB(image);
|
||||
for (uint32_t i = 1; i < imageCount; i++) {
|
||||
for (uint32_t i = 1; i < info.imageCount; i++) {
|
||||
lovrAssert(lovrImageGetWidth(images[0]) == lovrImageGetWidth(images[i]), "Image widths must match");
|
||||
lovrAssert(lovrImageGetHeight(images[0]) == lovrImageGetHeight(images[i]), "Image heights must match");
|
||||
lovrAssert(lovrImageGetFormat(images[0]) == lovrImageGetFormat(images[i]), "Image formats must match");
|
||||
lovrAssert(lovrImageGetLevelCount(images[0]) == lovrImageGetLevelCount(images[i]), "Image mipmap counts must match");
|
||||
lovrAssert(lovrImageGetLayerCount(images[i]) == 1, "When a list of images are provided, each must have a single layer");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -510,7 +514,7 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
info.type = lua_isnil(L, -1) ? info.type : luax_checkenum(L, -1, TextureType, NULL);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (imageCount == 0) {
|
||||
if (info.imageCount == 0) {
|
||||
lua_getfield(L, index, "format");
|
||||
info.format = lua_isnil(L, -1) ? info.format : luax_checkenum(L, -1, TextureFormat, NULL);
|
||||
lua_pop(L, 1);
|
||||
|
@ -546,7 +550,7 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case LUA_TNIL: info.usage = (imageCount == 0) ? TEXTURE_RENDER | TEXTURE_SAMPLE : TEXTURE_SAMPLE; break;
|
||||
case LUA_TNIL: info.usage = (info.imageCount == 0) ? TEXTURE_RENDER | TEXTURE_SAMPLE : TEXTURE_SAMPLE; break;
|
||||
default: return luaL_error(L, "Expected Texture usage to be a string, table, or nil");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
@ -556,18 +560,18 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
if (imageCount == 0 && info.depth == 0) {
|
||||
if (info.imageCount == 0 && info.depth == 0) {
|
||||
info.depth = info.type == TEXTURE_CUBE ? 6 : 1;
|
||||
}
|
||||
|
||||
Texture* texture = lovrTextureCreate(&info);
|
||||
|
||||
for (uint32_t i = 0; i < imageCount; i++) {
|
||||
lovrRelease(info.images[i], lovrImageDestroy);
|
||||
for (uint32_t i = 0; i < info.imageCount; i++) {
|
||||
lovrRelease(images[i], lovrImageDestroy);
|
||||
}
|
||||
|
||||
if (info.images != stack) {
|
||||
free(info.images);
|
||||
if (images != stack) {
|
||||
free(images);
|
||||
}
|
||||
|
||||
luax_pushtype(L, Texture, texture);
|
||||
|
|
|
@ -477,6 +477,97 @@ bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (info->upload.stream) {
|
||||
VkImage image = texture->handle;
|
||||
VkCommandBuffer commands = info->upload.stream->commands;
|
||||
uint32_t levelCount = info->upload.levelCount;
|
||||
gpu_buffer* buffer = info->upload.buffer;
|
||||
|
||||
VkPipelineStageFlags prev, next;
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkImageMemoryBarrier transition = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.image = image,
|
||||
.subresourceRange.aspectMask = texture->aspect,
|
||||
.subresourceRange.baseMipLevel = 0,
|
||||
.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.subresourceRange.baseArrayLayer = 0,
|
||||
.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS
|
||||
};
|
||||
|
||||
if (levelCount > 0) {
|
||||
VkBufferImageCopy regions[16];
|
||||
for (uint32_t i = 0; i < levelCount; i++) {
|
||||
regions[i] = (VkBufferImageCopy) {
|
||||
.bufferOffset = buffer->offset + info->upload.levelOffsets[i],
|
||||
.imageSubresource.aspectMask = texture->aspect,
|
||||
.imageSubresource.mipLevel = i,
|
||||
.imageSubresource.baseArrayLayer = 0,
|
||||
.imageSubresource.layerCount = texture->layered ? info->size[2] : 1,
|
||||
.imageExtent.width = MAX(info->size[0] >> i, 1),
|
||||
.imageExtent.height = MAX(info->size[1] >> i, 1),
|
||||
.imageExtent.depth = texture->layered ? 1 : MAX(info->size[2] >> i, 1)
|
||||
};
|
||||
}
|
||||
|
||||
// Upload initial contents
|
||||
prev = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
next = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
transition.srcAccessMask = 0;
|
||||
transition.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
transition.oldLayout = layout;
|
||||
transition.newLayout = layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
|
||||
vkCmdCopyBufferToImage(commands, buffer->handle, image, layout, levelCount, regions);
|
||||
|
||||
// Generate mipmaps
|
||||
if (info->upload.generateMipmaps) {
|
||||
prev = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
next = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
transition.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
transition.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
transition.subresourceRange.baseMipLevel = 0;
|
||||
transition.subresourceRange.levelCount = levelCount;
|
||||
transition.oldLayout = layout;
|
||||
transition.newLayout = layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
|
||||
for (uint32_t i = levelCount; i < info->mipmaps; i++) {
|
||||
VkImageBlit region = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = texture->aspect,
|
||||
.mipLevel = i - 1,
|
||||
.layerCount = texture->layered ? info->size[2] : 1
|
||||
},
|
||||
.dstSubresource = {
|
||||
.aspectMask = texture->aspect,
|
||||
.mipLevel = i,
|
||||
.layerCount = texture->layered ? info->size[2] : 1
|
||||
},
|
||||
.srcOffsets[1] = { MAX(info->size[0] >> (i - 1), 1), MAX(info->size[1] >> (i - 1), 1), 1 },
|
||||
.dstOffsets[1] = { MAX(info->size[0] >> i, 1), MAX(info->size[1] >> i, 1), 1 }
|
||||
};
|
||||
vkCmdBlitImage(commands, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_LINEAR);
|
||||
transition.subresourceRange.baseMipLevel = i;
|
||||
transition.subresourceRange.levelCount = 1;
|
||||
transition.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
transition.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transition to natural layout
|
||||
prev = levelCount > 0 ? VK_PIPELINE_STAGE_TRANSFER_BIT : VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
next = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
transition.srcAccessMask = levelCount > 0 ? VK_ACCESS_TRANSFER_WRITE_BIT : 0;
|
||||
transition.dstAccessMask = 0;
|
||||
transition.oldLayout = layout;
|
||||
transition.newLayout = texture->layout;
|
||||
transition.subresourceRange.baseMipLevel = 0;
|
||||
transition.subresourceRange.levelCount = info->mipmaps;
|
||||
vkCmdPipelineBarrier(commands, prev, next, 0, 0, NULL, 0, NULL, 1, &transition);
|
||||
}
|
||||
|
||||
texture->memory = memory - state.memory;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -292,7 +292,8 @@ Texture* lovrTextureCreate(TextureInfo* info) {
|
|||
};
|
||||
|
||||
uint32_t limit = limits[info->type];
|
||||
uint32_t mips = log2(MAX(MAX(info->width, info->height), (info->type == TEXTURE_3D ? info->depth : 1))) + 1;
|
||||
uint32_t mipmapCap = log2(MAX(MAX(info->width, info->height), (info->type == TEXTURE_3D ? info->depth : 1))) + 1;
|
||||
uint32_t mipmaps = CLAMP(info->mipmaps, 1, mipmapCap);
|
||||
uint8_t supports = state.features.formats[info->format];
|
||||
|
||||
lovrCheck(info->width > 0, "Texture width must be greater than zero");
|
||||
|
@ -305,18 +306,18 @@ Texture* lovrTextureCreate(TextureInfo* info) {
|
|||
lovrCheck(info->depth == 1 || info->type != TEXTURE_2D, "2D textures must have a depth of 1");
|
||||
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");
|
||||
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->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->mipmaps == 1, "Multisampled textures can only have 1 mipmap");
|
||||
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");
|
||||
lovrCheck(~info->usage & TEXTURE_STORAGE || (supports & GPU_FEATURE_STORAGE), "GPU does not support the 'storage' flag for this format");
|
||||
lovrCheck(~info->usage & TEXTURE_RENDER || info->width <= state.limits.renderSize[0], "Texture has 'render' flag but its size exceeds the renderSize limit");
|
||||
lovrCheck(~info->usage & TEXTURE_RENDER || info->height <= state.limits.renderSize[1], "Texture has 'render' flag but its size exceeds the renderSize limit");
|
||||
lovrCheck(info->mipmaps == ~0u || info->mipmaps <= mips, "Texture has more than the max number of mipmap levels for its size (%d)", mips);
|
||||
lovrCheck(mipmaps <= mipmapCap, "Texture has more than the max number of mipmap levels for its size (%d)", mipmapCap);
|
||||
lovrCheck((info->format < FORMAT_BC1 || info->format > FORMAT_BC7) || state.features.textureBC, "%s textures are not supported on this GPU", "BC");
|
||||
lovrCheck(info->format < FORMAT_ASTC_4x4 || state.features.textureASTC, "%s textures are not supported on this GPU", "ASTC");
|
||||
|
||||
|
@ -325,7 +326,41 @@ Texture* lovrTextureCreate(TextureInfo* info) {
|
|||
texture->ref = 1;
|
||||
texture->gpu = (gpu_texture*) (texture + 1);
|
||||
texture->info = *info;
|
||||
texture->info.mipmaps = CLAMP(texture->info.mipmaps, 1, mips);
|
||||
texture->info.mipmaps = mipmaps;
|
||||
|
||||
uint32_t levelCount = 0;
|
||||
uint32_t levelOffsets[16];
|
||||
uint32_t levelSizes[16];
|
||||
gpu_buffer* scratchpad;
|
||||
|
||||
if (info->imageCount > 0) {
|
||||
levelCount = lovrImageGetLevelCount(info->images[0]);
|
||||
lovrCheck(info->type != TEXTURE_3D || levelCount == 1, "Images used to initialize 3D textures can not have mipmaps");
|
||||
|
||||
uint32_t total = 0;
|
||||
for (uint32_t level = 0; level < levelCount; level++) {
|
||||
levelOffsets[level] = total;
|
||||
uint32_t width = MAX(info->width >> level, 1);
|
||||
uint32_t height = MAX(info->height >> level, 1);
|
||||
levelSizes[level] = measureTexture(info->format, width, height, info->depth);
|
||||
total += levelSizes[level];
|
||||
}
|
||||
|
||||
scratchpad = tempAlloc(gpu_sizeof_buffer());
|
||||
char* data = gpu_map(scratchpad, total, 64, GPU_MAP_WRITE);
|
||||
|
||||
for (uint32_t level = 0; level < levelCount; level++) {
|
||||
for (uint32_t layer = 0; layer < info->depth; layer++) {
|
||||
Image* image = info->imageCount == 1 ? info->images[0] : info->images[layer];
|
||||
uint32_t slice = info->imageCount == 1 ? layer : 0;
|
||||
uint32_t size = lovrImageGetLayerSize(image, level);
|
||||
lovrCheck(size == levelSizes[level], "Texture/Image size mismatch!");
|
||||
void* pixels = lovrImageGetLayerData(image, level, layer);
|
||||
memcpy(data, pixels, size);
|
||||
data += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpu_texture_init(texture->gpu, &(gpu_texture_info) {
|
||||
.type = (gpu_texture_type) info->type,
|
||||
|
@ -340,7 +375,14 @@ Texture* lovrTextureCreate(TextureInfo* info) {
|
|||
((info->usage & TEXTURE_COPY) ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0),
|
||||
.srgb = info->srgb,
|
||||
.handle = info->handle,
|
||||
.label = info->label
|
||||
.label = info->label,
|
||||
.upload = {
|
||||
.stream = getTransfers(),
|
||||
.buffer = scratchpad,
|
||||
.levelCount = levelCount,
|
||||
.levelOffsets = levelOffsets,
|
||||
.generateMipmaps = levelCount < mipmaps
|
||||
}
|
||||
});
|
||||
|
||||
// Automatically create a renderable view for renderable non-volume textures
|
||||
|
|
|
@ -182,6 +182,7 @@ typedef struct {
|
|||
uint32_t usage;
|
||||
bool srgb;
|
||||
uintptr_t handle;
|
||||
uint32_t imageCount;
|
||||
struct Image** images;
|
||||
const char* label;
|
||||
} TextureInfo;
|
||||
|
|
Loading…
Reference in New Issue