gpu: implement automatic mipmaps for images

All GPU APIs except OpenGL ES 2 can generate mipmaps for textures.
This trades 33% more GPU memory use for improved rendering quality
and speed for downscaled images.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-10-22 11:04:22 -06:00
parent e69ef4f0b4
commit bebc73db37
19 changed files with 233 additions and 38 deletions
+12
View File
@@ -685,6 +685,7 @@ const (
PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5
FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14
FILTER_MIN_MAG_MIP_LINEAR = 0x15
FILTER_MIN_MAG_MIP_POINT = 0
TEXTURE_ADDRESS_MIRROR = 2
@@ -701,6 +702,7 @@ const (
BUFFEREX_SRV_FLAG_RAW = 0x1
RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS = 0x20
RESOURCE_MISC_GENERATE_MIPS = 0x1
CREATE_DEVICE_DEBUG = 0x2
@@ -1190,6 +1192,16 @@ func (s *IDXGISwapChain) GetBuffer(index int, riid *GUID) (*IUnknown, error) {
return buf, nil
}
func (c *DeviceContext) GenerateMips(res *ShaderResourceView) {
syscall.Syscall(
c.Vtbl.GenerateMips,
2,
uintptr(unsafe.Pointer(c)),
uintptr(unsafe.Pointer(res)),
0,
)
}
func (c *DeviceContext) Unmap(resource *Resource, subResource uint32) {
syscall.Syscall(
c.Vtbl.Unmap,
+1
View File
@@ -56,6 +56,7 @@ const (
GREATER = 0x204
GEQUAL = 0x206
LINEAR = 0x2601
LINEAR_MIPMAP_LINEAR = 0x2703
LINK_STATUS = 0x8b82
LUMINANCE = 0x1909
MAP_READ_BIT = 0x0001
+5
View File
@@ -72,6 +72,7 @@ type Functions struct {
_flush js.Value
_framebufferRenderbuffer js.Value
_framebufferTexture2D js.Value
_generateMipmap js.Value
_getRenderbufferParameteri js.Value
_getFramebufferAttachmentParameter js.Value
_getParameter js.Value
@@ -167,6 +168,7 @@ func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
_flush: _bind(webgl, `flush`),
_framebufferRenderbuffer: _bind(webgl, `framebufferRenderbuffer`),
_framebufferTexture2D: _bind(webgl, `framebufferTexture2D`),
_generateMipmap: _bind(webgl, `generateMipmap`),
_getRenderbufferParameteri: _bind(webgl, `getRenderbufferParameteri`),
_getFramebufferAttachmentParameter: _bind(webgl, `getFramebufferAttachmentParameter`),
_getParameter: _bind(webgl, `getParameter`),
@@ -419,6 +421,9 @@ func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarg
func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
f._framebufferTexture2D.Invoke(int(target), int(attachment), int(texTarget), js.Value(t), level)
}
func (f *Functions) GenerateMipmap(target Enum) {
f._generateMipmap.Invoke(int(target))
}
func (f *Functions) GetError() Enum {
// Avoid slow getError calls. See gio#179.
return 0
+11
View File
@@ -72,6 +72,7 @@ typedef void (*_glFlush)(void);
typedef void (*_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef void (*_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
typedef void (*_glGenBuffers)(GLsizei n, GLuint *buffers);
typedef void (*_glGenerateMipmap)(GLenum target);
typedef void (*_glGenFramebuffers)(GLsizei n, GLuint *framebuffers);
typedef void (*_glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers);
typedef void (*_glGenTextures)(GLsizei n, GLuint *textures);
@@ -286,6 +287,10 @@ static void glGenBuffers(_glGenBuffers f, GLsizei n, GLuint *buffers) {
f(n, buffers);
}
static void glGenerateMipmap(_glGenerateMipmap f, GLenum target) {
f(target);
}
static void glGenFramebuffers(_glGenFramebuffers f, GLsizei n, GLuint *framebuffers) {
f(n, framebuffers);
}
@@ -561,6 +566,7 @@ type Functions struct {
glFramebufferRenderbuffer C._glFramebufferRenderbuffer
glFramebufferTexture2D C._glFramebufferTexture2D
glGenBuffers C._glGenBuffers
glGenerateMipmap C._glGenerateMipmap
glGenFramebuffers C._glGenFramebuffers
glGenRenderbuffers C._glGenRenderbuffers
glGenTextures C._glGenTextures
@@ -723,6 +729,7 @@ func (f *Functions) load(forceES bool) error {
f.glFramebufferRenderbuffer = must("glFramebufferRenderbuffer")
f.glFramebufferTexture2D = must("glFramebufferTexture2D")
f.glGenBuffers = must("glGenBuffers")
f.glGenerateMipmap = must("glGenerateMipmap")
f.glGenFramebuffers = must("glGenFramebuffers")
f.glGenRenderbuffers = must("glGenRenderbuffers")
f.glGenTextures = must("glGenTextures")
@@ -1048,6 +1055,10 @@ func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t T
C.glFramebufferTexture2D(f.glFramebufferTexture2D, C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(t.V), C.GLint(level))
}
func (f *Functions) GenerateMipmap(target Enum) {
C.glGenerateMipmap(f.glGenerateMipmap, C.GLenum(target))
}
func (c *Functions) GetBinding(pname Enum) Object {
return Object{uint(c.GetInteger(pname))}
}
+4
View File
@@ -35,6 +35,7 @@ var (
_glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays")
_glCompileShader = LibGLESv2.NewProc("glCompileShader")
_glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D")
_glGenerateMipmap = LibGLESv2.NewProc("glGenerateMipmap")
_glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
_glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
_glGenVertexArrays = LibGLESv2.NewProc("glGenVertexArrays")
@@ -192,6 +193,9 @@ func (c *Functions) CompileShader(s Shader) {
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
syscall.Syscall9(_glCopyTexSubImage2D.Addr(), 8, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0)
}
func (f *Functions) GenerateMipmap(target Enum) {
syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 0)
}
func (c *Functions) CreateBuffer() Buffer {
var buf uintptr
syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
+56 -10
View File
@@ -303,6 +303,10 @@ static VkResult vkResetDescriptorPool(PFN_vkResetDescriptorPool f, VkDevice devi
return f(device, descriptorPool, flags);
}
static void vkCmdBlitImage(PFN_vkCmdBlitImage f, VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter) {
f(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter);
}
static void vkCmdCopyImage(PFN_vkCmdCopyImage f, VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions) {
f(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
}
@@ -415,6 +419,7 @@ type (
Queue = C.VkQueue
IndexType = C.VkIndexType
Image = C.VkImage
ImageBlit = C.VkImageBlit
ImageCopy = C.VkImageCopy
ImageLayout = C.VkImageLayout
ImageMemoryBarrier = C.VkImageMemoryBarrier
@@ -482,9 +487,10 @@ const (
FORMAT_R32G32B32_SFLOAT Format = C.VK_FORMAT_R32G32B32_SFLOAT
FORMAT_R32G32B32A32_SFLOAT Format = C.VK_FORMAT_R32G32B32A32_SFLOAT
FORMAT_FEATURE_COLOR_ATTACHMENT_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT
FORMAT_FEATURE_SAMPLED_IMAGE_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
FORMAT_FEATURE_COLOR_ATTACHMENT_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT
FORMAT_FEATURE_SAMPLED_IMAGE_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT
IMAGE_USAGE_SAMPLED_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_SAMPLED_BIT
IMAGE_USAGE_COLOR_ATTACHMENT_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
@@ -574,6 +580,11 @@ const (
QUEUE_GRAPHICS_BIT QueueFlags = C.VK_QUEUE_GRAPHICS_BIT
QUEUE_COMPUTE_BIT QueueFlags = C.VK_QUEUE_COMPUTE_BIT
SAMPLER_MIPMAP_MODE_NEAREST SamplerMipmapMode = C.VK_SAMPLER_MIPMAP_MODE_NEAREST
SAMPLER_MIPMAP_MODE_LINEAR SamplerMipmapMode = C.VK_SAMPLER_MIPMAP_MODE_LINEAR
REMAINING_MIP_LEVELS = -1
)
var (
@@ -654,6 +665,7 @@ var funcs struct {
vkFreeDescriptorSets C.PFN_vkFreeDescriptorSets
vkUpdateDescriptorSets C.PFN_vkUpdateDescriptorSets
vkResetDescriptorPool C.PFN_vkResetDescriptorPool
vkCmdBlitImage C.PFN_vkCmdBlitImage
vkCmdCopyImage C.PFN_vkCmdCopyImage
vkCreateComputePipelines C.PFN_vkCreateComputePipelines
vkCreateFence C.PFN_vkCreateFence
@@ -794,6 +806,7 @@ func vkInit() error {
funcs.vkFreeDescriptorSets = must("vkFreeDescriptorSets")
funcs.vkUpdateDescriptorSets = must("vkUpdateDescriptorSets")
funcs.vkResetDescriptorPool = must("vkResetDescriptorPool")
funcs.vkCmdBlitImage = must("vkCmdBlitImage")
funcs.vkCmdCopyImage = must("vkCmdCopyImage")
funcs.vkCreateComputePipelines = must("vkCreateComputePipelines")
funcs.vkCreateFence = must("vkCreateFence")
@@ -1376,6 +1389,13 @@ func CmdBindDescriptorSets(cmdBuf CommandBuffer, point PipelineBindPoint, layout
C.vkCmdBindDescriptorSets(funcs.vkCmdBindDescriptorSets, cmdBuf, point, layout, C.uint32_t(firstSet), C.uint32_t(len(sets)), &sets[0], 0, nil)
}
func CmdBlitImage(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Image, dstLayout ImageLayout, regions []ImageBlit, filter Filter) {
if len(regions) == 0 {
return
}
C.vkCmdBlitImage(funcs.vkCmdBlitImage, cmdBuf, src, srcLayout, dst, dstLayout, C.uint32_t(len(regions)), &regions[0], filter)
}
func CmdCopyImage(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Image, dstLayout ImageLayout, regions []ImageCopy) {
if len(regions) == 0 {
return
@@ -1394,7 +1414,7 @@ func CmdDispatch(cmdBuf CommandBuffer, x, y, z int) {
C.vkCmdDispatch(funcs.vkCmdDispatch, cmdBuf, C.uint32_t(x), C.uint32_t(y), C.uint32_t(z))
}
func CreateImage(pd PhysicalDevice, d Device, format Format, width, height int, usage ImageUsageFlags) (Image, DeviceMemory, error) {
func CreateImage(pd PhysicalDevice, d Device, format Format, width, height, mipmaps int, usage ImageUsageFlags) (Image, DeviceMemory, error) {
inf := C.VkImageCreateInfo{
sType: C.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
imageType: C.VK_IMAGE_TYPE_2D,
@@ -1404,7 +1424,7 @@ func CreateImage(pd PhysicalDevice, d Device, format Format, width, height int,
height: C.uint32_t(height),
depth: 1,
},
mipLevels: 1,
mipLevels: C.uint32_t(mipmaps),
arrayLayers: 1,
samples: C.VK_SAMPLE_COUNT_1_BIT,
tiling: C.VK_IMAGE_TILING_OPTIMAL,
@@ -1451,11 +1471,13 @@ func FreeMemory(d Device, mem DeviceMemory) {
C.vkFreeMemory(funcs.vkFreeMemory, d, mem, nil)
}
func CreateSampler(d Device, minFilter, magFilter Filter) (Sampler, error) {
func CreateSampler(d Device, minFilter, magFilter Filter, mipmapMode SamplerMipmapMode) (Sampler, error) {
inf := C.VkSamplerCreateInfo{
sType: C.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
minFilter: minFilter,
magFilter: magFilter,
mipmapMode: mipmapMode,
maxLod: C.VK_LOD_CLAMP_NONE,
addressModeU: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
addressModeV: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
}
@@ -1905,7 +1927,7 @@ func BuildViewport(x, y, width, height float32) Viewport {
}
}
func BuildImageMemoryBarrier(img Image, srcMask, dstMask AccessFlags, oldLayout, newLayout ImageLayout) ImageMemoryBarrier {
func BuildImageMemoryBarrier(img Image, srcMask, dstMask AccessFlags, oldLayout, newLayout ImageLayout, baseMip, numMips int) ImageMemoryBarrier {
return C.VkImageMemoryBarrier{
sType: C.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
srcAccessMask: srcMask,
@@ -1914,9 +1936,10 @@ func BuildImageMemoryBarrier(img Image, srcMask, dstMask AccessFlags, oldLayout,
newLayout: newLayout,
image: img,
subresourceRange: C.VkImageSubresourceRange{
aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
levelCount: C.VK_REMAINING_MIP_LEVELS,
layerCount: C.VK_REMAINING_ARRAY_LAYERS,
aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
baseMipLevel: C.uint32_t(baseMip),
levelCount: C.uint32_t(numMips),
layerCount: C.VK_REMAINING_ARRAY_LAYERS,
},
}
}
@@ -1982,6 +2005,29 @@ func BuildImageCopy(srcX, srcY, dstX, dstY, width, height int) ImageCopy {
}
}
func BuildImageBlit(srcX, srcY, dstX, dstY, srcWidth, srcHeight, dstWidth, dstHeight, srcMip, dstMip int) ImageBlit {
return C.VkImageBlit{
srcOffsets: [2]C.VkOffset3D{
{C.int32_t(srcX), C.int32_t(srcY), 0},
{C.int32_t(srcWidth), C.int32_t(srcHeight), 1},
},
srcSubresource: C.VkImageSubresourceLayers{
aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
layerCount: 1,
mipLevel: C.uint32_t(srcMip),
},
dstOffsets: [2]C.VkOffset3D{
{C.int32_t(dstX), C.int32_t(dstY), 0},
{C.int32_t(dstWidth), C.int32_t(dstHeight), 1},
},
dstSubresource: C.VkImageSubresourceLayers{
aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
layerCount: 1,
mipLevel: C.uint32_t(dstMip),
},
}
}
func findMemoryTypeIndex(pd C.VkPhysicalDevice, constraints C.uint32_t, wantProps C.VkMemoryPropertyFlags) (int, bool) {
var memProps C.VkPhysicalDeviceMemoryProperties
C.vkGetPhysicalDeviceMemoryProperties(funcs.vkGetPhysicalDeviceMemoryProperties, pd, &memProps)