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>
@@ -436,7 +436,7 @@ func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Text
|
|||||||
if tex.tex != nil {
|
if tex.tex != nil {
|
||||||
return tex.tex
|
return tex.tex
|
||||||
}
|
}
|
||||||
handle, err := r.ctx.NewTexture(driver.TextureFormatSRGBA, data.src.Bounds().Dx(), data.src.Bounds().Dy(), driver.FilterLinear, driver.FilterLinear, driver.BufferBindingTexture)
|
handle, err := r.ctx.NewTexture(driver.TextureFormatSRGBA, data.src.Bounds().Dx(), data.src.Bounds().Dy(), driver.FilterLinearMipmapLinear, driver.FilterLinear, driver.BufferBindingTexture)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"math"
|
"math"
|
||||||
|
"math/bits"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@@ -58,6 +59,7 @@ type Texture struct {
|
|||||||
|
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
mipmap bool
|
||||||
foreign bool
|
foreign bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,17 +221,33 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported texture format %d", format)
|
return nil, fmt.Errorf("unsupported texture format %d", format)
|
||||||
}
|
}
|
||||||
|
bindFlags := convBufferBinding(bindings)
|
||||||
|
miscFlags := uint32(0)
|
||||||
|
mipmap := minFilter == driver.FilterLinearMipmapLinear
|
||||||
|
nmipmaps := 1
|
||||||
|
if mipmap {
|
||||||
|
// Flags required by ID3D11DeviceContext::GenerateMips.
|
||||||
|
bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET
|
||||||
|
miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS
|
||||||
|
dim := width
|
||||||
|
if height > dim {
|
||||||
|
dim = height
|
||||||
|
}
|
||||||
|
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
|
||||||
|
nmipmaps = log2 + 1
|
||||||
|
}
|
||||||
tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
|
tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
|
||||||
Width: uint32(width),
|
Width: uint32(width),
|
||||||
Height: uint32(height),
|
Height: uint32(height),
|
||||||
MipLevels: 1,
|
MipLevels: uint32(nmipmaps),
|
||||||
ArraySize: 1,
|
ArraySize: 1,
|
||||||
Format: d3dfmt,
|
Format: d3dfmt,
|
||||||
SampleDesc: d3d11.DXGI_SAMPLE_DESC{
|
SampleDesc: d3d11.DXGI_SAMPLE_DESC{
|
||||||
Count: 1,
|
Count: 1,
|
||||||
Quality: 0,
|
Quality: 0,
|
||||||
},
|
},
|
||||||
BindFlags: convBufferBinding(bindings),
|
BindFlags: bindFlags,
|
||||||
|
MiscFlags: miscFlags,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -247,6 +265,8 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
filter = d3d11.FILTER_MIN_MAG_MIP_POINT
|
filter = d3d11.FILTER_MIN_MAG_MIP_POINT
|
||||||
case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
|
case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
|
||||||
filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
|
filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
|
||||||
|
case minFilter == driver.FilterLinearMipmapLinear && magFilter == driver.FilterLinear:
|
||||||
|
filter = d3d11.FILTER_MIN_MAG_MIP_LINEAR
|
||||||
default:
|
default:
|
||||||
d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
|
d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
|
||||||
return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
|
return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
|
||||||
@@ -325,7 +345,7 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height}, nil
|
return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height, mipmap: mipmap}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
|
func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
|
||||||
@@ -609,6 +629,9 @@ func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
|
|||||||
}
|
}
|
||||||
res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
|
res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
|
||||||
t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
|
t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
|
||||||
|
if t.mipmap {
|
||||||
|
t.backend.ctx.GenerateMips(t.resView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Texture) Release() {
|
func (t *Texture) Release() {
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
FilterNearest TextureFilter = iota
|
FilterNearest TextureFilter = iota
|
||||||
FilterLinear
|
FilterLinear
|
||||||
|
FilterLinearMipmapLinear
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -239,6 +239,14 @@ static void blitEncCopyBufferToTexture(CFTypeRef blitEncRef, CFTypeRef bufRef, C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void blitEncGenerateMipmapsForTexture(CFTypeRef blitEncRef, CFTypeRef texRef) {
|
||||||
|
@autoreleasepool {
|
||||||
|
id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
|
||||||
|
id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
|
||||||
|
[enc generateMipmapsForTexture: tex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void blitEncCopyTextureToBuffer(CFTypeRef blitEncRef, CFTypeRef texRef, CFTypeRef bufRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
|
static void blitEncCopyTextureToBuffer(CFTypeRef blitEncRef, CFTypeRef texRef, CFTypeRef bufRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
|
id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
|
||||||
@@ -269,25 +277,26 @@ static void blitEncCopyBufferToBuffer(CFTypeRef blitEncRef, CFTypeRef srcRef, CF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static CFTypeRef newTexture(CFTypeRef devRef, NSUInteger width, NSUInteger height, MTLPixelFormat format, MTLTextureUsage usage) {
|
static CFTypeRef newTexture(CFTypeRef devRef, NSUInteger width, NSUInteger height, MTLPixelFormat format, MTLTextureUsage usage, int mipmapped) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
|
id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
|
||||||
MTLTextureDescriptor *mtlDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
|
MTLTextureDescriptor *mtlDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
|
||||||
width: width
|
width: width
|
||||||
height: height
|
height: height
|
||||||
mipmapped: NO];
|
mipmapped: mipmapped ? YES : NO];
|
||||||
mtlDesc.usage = usage;
|
mtlDesc.usage = usage;
|
||||||
mtlDesc.storageMode = MTLStorageModePrivate;
|
mtlDesc.storageMode = MTLStorageModePrivate;
|
||||||
return CFBridgingRetain([dev newTextureWithDescriptor:mtlDesc]);
|
return CFBridgingRetain([dev newTextureWithDescriptor:mtlDesc]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static CFTypeRef newSampler(CFTypeRef devRef, MTLSamplerMinMagFilter minFilter, MTLSamplerMinMagFilter magFilter) {
|
static CFTypeRef newSampler(CFTypeRef devRef, MTLSamplerMinMagFilter minFilter, MTLSamplerMinMagFilter magFilter, MTLSamplerMipFilter mipFilter) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
|
id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
|
||||||
MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
|
MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
|
||||||
desc.minFilter = minFilter;
|
desc.minFilter = minFilter;
|
||||||
desc.magFilter = magFilter;
|
desc.magFilter = magFilter;
|
||||||
|
desc.mipFilter = mipFilter;
|
||||||
return CFBridgingRetain([dev newSamplerStateWithDescriptor:desc]);
|
return CFBridgingRetain([dev newSamplerStateWithDescriptor:desc]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,6 +414,7 @@ type Texture struct {
|
|||||||
sampler C.CFTypeRef
|
sampler C.CFTypeRef
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
mipmap bool
|
||||||
foreign bool
|
foreign bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,26 +591,33 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
if bindings&driver.BufferBindingShaderStorageWrite != 0 {
|
if bindings&driver.BufferBindingShaderStorageWrite != 0 {
|
||||||
usage |= C.MTLTextureUsageShaderWrite
|
usage |= C.MTLTextureUsageShaderWrite
|
||||||
}
|
}
|
||||||
tex := C.newTexture(b.dev, C.NSUInteger(width), C.NSUInteger(height), mformat, usage)
|
min, mip := samplerFilterFor(minFilter)
|
||||||
|
max, _ := samplerFilterFor(magFilter)
|
||||||
|
mipmap := mip != C.MTLSamplerMipFilterNotMipmapped
|
||||||
|
mipmapped := C.int(0)
|
||||||
|
if mipmap {
|
||||||
|
mipmapped = 1
|
||||||
|
}
|
||||||
|
tex := C.newTexture(b.dev, C.NSUInteger(width), C.NSUInteger(height), mformat, usage, mipmapped)
|
||||||
if tex == 0 {
|
if tex == 0 {
|
||||||
return nil, errors.New("metal: [MTLDevice newTextureWithDescriptor:] failed")
|
return nil, errors.New("metal: [MTLDevice newTextureWithDescriptor:] failed")
|
||||||
}
|
}
|
||||||
min := samplerFilterFor(minFilter)
|
s := C.newSampler(b.dev, min, max, mip)
|
||||||
max := samplerFilterFor(magFilter)
|
|
||||||
s := C.newSampler(b.dev, min, max)
|
|
||||||
if s == 0 {
|
if s == 0 {
|
||||||
C.CFRelease(tex)
|
C.CFRelease(tex)
|
||||||
return nil, errors.New("metal: [MTLDevice newSamplerStateWithDescriptor:] failed")
|
return nil, errors.New("metal: [MTLDevice newSamplerStateWithDescriptor:] failed")
|
||||||
}
|
}
|
||||||
return &Texture{backend: b, texture: tex, sampler: s, width: width, height: height}, nil
|
return &Texture{backend: b, texture: tex, sampler: s, width: width, height: height, mipmap: mipmap}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func samplerFilterFor(f driver.TextureFilter) C.MTLSamplerMinMagFilter {
|
func samplerFilterFor(f driver.TextureFilter) (C.MTLSamplerMinMagFilter, C.MTLSamplerMipFilter) {
|
||||||
switch f {
|
switch f {
|
||||||
case driver.FilterNearest:
|
case driver.FilterNearest:
|
||||||
return C.MTLSamplerMinMagFilterNearest
|
return C.MTLSamplerMinMagFilterNearest, C.MTLSamplerMipFilterNotMipmapped
|
||||||
case driver.FilterLinear:
|
case driver.FilterLinear:
|
||||||
return C.MTLSamplerMinMagFilterLinear
|
return C.MTLSamplerMinMagFilterLinear, C.MTLSamplerMipFilterNotMipmapped
|
||||||
|
case driver.FilterLinearMipmapLinear:
|
||||||
|
return C.MTLSamplerMinMagFilterLinear, C.MTLSamplerMipFilterLinear
|
||||||
default:
|
default:
|
||||||
panic("invalid texture filter")
|
panic("invalid texture filter")
|
||||||
}
|
}
|
||||||
@@ -900,6 +917,9 @@ func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
|
|||||||
depth: 1,
|
depth: 1,
|
||||||
}
|
}
|
||||||
C.blitEncCopyBufferToTexture(enc, buf, t.texture, C.NSUInteger(off), C.NSUInteger(dstStride), C.NSUInteger(len(store)), msize, orig)
|
C.blitEncCopyBufferToTexture(enc, buf, t.texture, C.NSUInteger(off), C.NSUInteger(dstStride), C.NSUInteger(len(store)), msize, orig)
|
||||||
|
if t.mipmap {
|
||||||
|
C.blitEncGenerateMipmapsForTexture(enc, t.texture)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Texture) Release() {
|
func (t *Texture) Release() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"math/bits"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -105,6 +106,7 @@ type texture struct {
|
|||||||
triple textureTriple
|
triple textureTriple
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
mipmap bool
|
||||||
bindings driver.BufferBinding
|
bindings driver.BufferBinding
|
||||||
foreign bool
|
foreign bool
|
||||||
}
|
}
|
||||||
@@ -695,13 +697,29 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
return nil, errors.New("unsupported texture format")
|
return nil, errors.New("unsupported texture format")
|
||||||
}
|
}
|
||||||
b.BindTexture(0, tex)
|
b.BindTexture(0, tex)
|
||||||
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, toTexFilter(magFilter))
|
min, mipmap := toTexFilter(minFilter)
|
||||||
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, toTexFilter(minFilter))
|
mag, _ := toTexFilter(magFilter)
|
||||||
|
if b.gles && b.glver[0] < 3 {
|
||||||
|
// OpenGL ES 2 only supports mipmaps for power-of-two textures.
|
||||||
|
mipmap = false
|
||||||
|
}
|
||||||
|
tex.mipmap = mipmap
|
||||||
|
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, mag)
|
||||||
|
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min)
|
||||||
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||||
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||||
if b.gles && b.glver[0] >= 3 {
|
if mipmap {
|
||||||
|
nmipmaps := 1
|
||||||
|
if mipmap {
|
||||||
|
dim := width
|
||||||
|
if height > dim {
|
||||||
|
dim = height
|
||||||
|
}
|
||||||
|
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
|
||||||
|
nmipmaps = log2 + 1
|
||||||
|
}
|
||||||
// Immutable textures are required for BindImageTexture, and can't hurt otherwise.
|
// Immutable textures are required for BindImageTexture, and can't hurt otherwise.
|
||||||
b.funcs.TexStorage2D(gl.TEXTURE_2D, 1, tex.triple.internalFormat, width, height)
|
b.funcs.TexStorage2D(gl.TEXTURE_2D, nmipmaps, tex.triple.internalFormat, width, height)
|
||||||
} else {
|
} else {
|
||||||
b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ)
|
b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ)
|
||||||
}
|
}
|
||||||
@@ -1195,12 +1213,14 @@ func (p *pipeline) Release() {
|
|||||||
*p = pipeline{}
|
*p = pipeline{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toTexFilter(f driver.TextureFilter) int {
|
func toTexFilter(f driver.TextureFilter) (int, bool) {
|
||||||
switch f {
|
switch f {
|
||||||
case driver.FilterNearest:
|
case driver.FilterNearest:
|
||||||
return gl.NEAREST
|
return gl.NEAREST, false
|
||||||
case driver.FilterLinear:
|
case driver.FilterLinear:
|
||||||
return gl.LINEAR
|
return gl.LINEAR, false
|
||||||
|
case driver.FilterLinearMipmapLinear:
|
||||||
|
return gl.LINEAR_MIPMAP_LINEAR, true
|
||||||
default:
|
default:
|
||||||
panic("unsupported texture filter")
|
panic("unsupported texture filter")
|
||||||
}
|
}
|
||||||
@@ -1234,6 +1254,9 @@ func (t *texture) Upload(offset, size image.Point, pixels []byte, stride int) {
|
|||||||
}
|
}
|
||||||
t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, rowLen)
|
t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, rowLen)
|
||||||
t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels)
|
t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels)
|
||||||
|
if t.mipmap {
|
||||||
|
t.backend.funcs.GenerateMipmap(gl.TEXTURE_2D)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *timer) Begin() {
|
func (t *timer) Begin() {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 491 B After Width: | Height: | Size: 569 B |
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 402 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 386 B |
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 304 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 383 B |
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 791 B |
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 1.0 KiB |
@@ -10,6 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
"gioui.org/gpu/internal/driver"
|
"gioui.org/gpu/internal/driver"
|
||||||
"gioui.org/internal/vk"
|
"gioui.org/internal/vk"
|
||||||
@@ -75,6 +76,7 @@ type Texture struct {
|
|||||||
sampler vk.Sampler
|
sampler vk.Sampler
|
||||||
fbo vk.Framebuffer
|
fbo vk.Framebuffer
|
||||||
format vk.Format
|
format vk.Format
|
||||||
|
mipmaps int
|
||||||
layout vk.ImageLayout
|
layout vk.ImageLayout
|
||||||
passLayout vk.ImageLayout
|
passLayout vk.ImageLayout
|
||||||
width int
|
width int
|
||||||
@@ -155,7 +157,7 @@ func newVulkanDevice(api driver.Vulkan) (driver.Device, error) {
|
|||||||
if props&reqs == reqs {
|
if props&reqs == reqs {
|
||||||
b.caps |= driver.FeatureFloatRenderTargets
|
b.caps |= driver.FeatureFloatRenderTargets
|
||||||
}
|
}
|
||||||
reqs = vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT
|
reqs = vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT
|
||||||
props = vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R8G8B8A8_SRGB)
|
props = vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R8G8B8A8_SRGB)
|
||||||
if props&reqs == reqs {
|
if props&reqs == reqs {
|
||||||
b.caps |= driver.FeatureSRGB
|
b.caps |= driver.FeatureSRGB
|
||||||
@@ -292,19 +294,31 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
usage |= vk.IMAGE_USAGE_STORAGE_BIT
|
usage |= vk.IMAGE_USAGE_STORAGE_BIT
|
||||||
}
|
}
|
||||||
filterFor := func(f driver.TextureFilter) vk.Filter {
|
filterFor := func(f driver.TextureFilter) vk.Filter {
|
||||||
switch minFilter {
|
switch f {
|
||||||
case driver.FilterLinear:
|
case driver.FilterLinear, driver.FilterLinearMipmapLinear:
|
||||||
return vk.FILTER_LINEAR
|
return vk.FILTER_LINEAR
|
||||||
case driver.FilterNearest:
|
case driver.FilterNearest:
|
||||||
return vk.FILTER_NEAREST
|
return vk.FILTER_NEAREST
|
||||||
}
|
}
|
||||||
panic("unknown filter")
|
panic("unknown filter")
|
||||||
}
|
}
|
||||||
sampler, err := vk.CreateSampler(b.dev, filterFor(minFilter), filterFor(magFilter))
|
mipmapMode := vk.SAMPLER_MIPMAP_MODE_NEAREST
|
||||||
|
mipmap := minFilter == driver.FilterLinearMipmapLinear
|
||||||
|
nmipmaps := 1
|
||||||
|
if mipmap {
|
||||||
|
mipmapMode = vk.SAMPLER_MIPMAP_MODE_LINEAR
|
||||||
|
dim := width
|
||||||
|
if height > dim {
|
||||||
|
dim = height
|
||||||
|
}
|
||||||
|
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
|
||||||
|
nmipmaps = log2 + 1
|
||||||
|
}
|
||||||
|
sampler, err := vk.CreateSampler(b.dev, filterFor(minFilter), filterFor(magFilter), mipmapMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mapErr(err)
|
return nil, mapErr(err)
|
||||||
}
|
}
|
||||||
img, mem, err := vk.CreateImage(b.physDev, b.dev, vkfmt, width, height, usage)
|
img, mem, err := vk.CreateImage(b.physDev, b.dev, vkfmt, width, height, nmipmaps, usage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
vk.DestroySampler(b.dev, sampler)
|
vk.DestroySampler(b.dev, sampler)
|
||||||
return nil, mapErr(err)
|
return nil, mapErr(err)
|
||||||
@@ -316,7 +330,7 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
|
|||||||
vk.FreeMemory(b.dev, mem)
|
vk.FreeMemory(b.dev, mem)
|
||||||
return nil, mapErr(err)
|
return nil, mapErr(err)
|
||||||
}
|
}
|
||||||
t := &Texture{backend: b, img: img, mem: mem, view: view, sampler: sampler, layout: vk.IMAGE_LAYOUT_UNDEFINED, passLayout: passLayout, width: width, height: height, format: vkfmt}
|
t := &Texture{backend: b, img: img, mem: mem, view: view, sampler: sampler, layout: vk.IMAGE_LAYOUT_UNDEFINED, passLayout: passLayout, width: width, height: height, format: vkfmt, mipmaps: nmipmaps}
|
||||||
if bindings&driver.BufferBindingFramebuffer != 0 {
|
if bindings&driver.BufferBindingFramebuffer != 0 {
|
||||||
pass, err := vk.CreateRenderPass(b.dev, vkfmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE,
|
pass, err := vk.CreateRenderPass(b.dev, vkfmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil)
|
vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil)
|
||||||
@@ -646,6 +660,40 @@ func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
|
|||||||
vk.ACCESS_TRANSFER_WRITE_BIT,
|
vk.ACCESS_TRANSFER_WRITE_BIT,
|
||||||
)
|
)
|
||||||
vk.CmdCopyBufferToImage(cmdBuf, stage.buf, t.img, t.layout, op)
|
vk.CmdCopyBufferToImage(cmdBuf, stage.buf, t.img, t.layout, op)
|
||||||
|
// Build mipmaps by repeating linear blits.
|
||||||
|
w, h := t.width, t.height
|
||||||
|
for i := 1; i < t.mipmaps; i++ {
|
||||||
|
nw, nh := w/2, h/2
|
||||||
|
if nh < 1 {
|
||||||
|
nh = 1
|
||||||
|
}
|
||||||
|
if nw < 1 {
|
||||||
|
nw = 1
|
||||||
|
}
|
||||||
|
// Transition previous (source) level.
|
||||||
|
b := vk.BuildImageMemoryBarrier(
|
||||||
|
t.img,
|
||||||
|
vk.ACCESS_TRANSFER_WRITE_BIT, vk.ACCESS_TRANSFER_READ_BIT,
|
||||||
|
vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
i-1, 1,
|
||||||
|
)
|
||||||
|
vk.CmdPipelineBarrier(cmdBuf, vk.PIPELINE_STAGE_TRANSFER_BIT, vk.PIPELINE_STAGE_TRANSFER_BIT, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b})
|
||||||
|
// Blit to this mipmap level.
|
||||||
|
blit := vk.BuildImageBlit(0, 0, 0, 0, w, h, nw, nh, i-1, i)
|
||||||
|
vk.CmdBlitImage(cmdBuf, t.img, vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, t.img, vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, []vk.ImageBlit{blit}, vk.FILTER_LINEAR)
|
||||||
|
w, h = nw, nh
|
||||||
|
}
|
||||||
|
if t.mipmaps > 1 {
|
||||||
|
// Add barrier for last blit.
|
||||||
|
b := vk.BuildImageMemoryBarrier(
|
||||||
|
t.img,
|
||||||
|
vk.ACCESS_TRANSFER_WRITE_BIT, vk.ACCESS_TRANSFER_READ_BIT,
|
||||||
|
vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
t.mipmaps-1, 1,
|
||||||
|
)
|
||||||
|
vk.CmdPipelineBarrier(cmdBuf, vk.PIPELINE_STAGE_TRANSFER_BIT, vk.PIPELINE_STAGE_TRANSFER_BIT, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b})
|
||||||
|
t.layout = vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Texture) Release() {
|
func (t *Texture) Release() {
|
||||||
@@ -756,6 +804,7 @@ func (t *Texture) imageBarrier(cmdBuf vk.CommandBuffer, layout vk.ImageLayout, s
|
|||||||
t.img,
|
t.img,
|
||||||
t.scope.access, access,
|
t.scope.access, access,
|
||||||
t.layout, layout,
|
t.layout, layout,
|
||||||
|
0, vk.REMAINING_MIP_LEVELS,
|
||||||
)
|
)
|
||||||
vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b})
|
vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b})
|
||||||
t.layout = layout
|
t.layout = layout
|
||||||
|
|||||||
@@ -685,6 +685,7 @@ const (
|
|||||||
PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5
|
PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5
|
||||||
|
|
||||||
FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14
|
FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14
|
||||||
|
FILTER_MIN_MAG_MIP_LINEAR = 0x15
|
||||||
FILTER_MIN_MAG_MIP_POINT = 0
|
FILTER_MIN_MAG_MIP_POINT = 0
|
||||||
|
|
||||||
TEXTURE_ADDRESS_MIRROR = 2
|
TEXTURE_ADDRESS_MIRROR = 2
|
||||||
@@ -701,6 +702,7 @@ const (
|
|||||||
BUFFEREX_SRV_FLAG_RAW = 0x1
|
BUFFEREX_SRV_FLAG_RAW = 0x1
|
||||||
|
|
||||||
RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS = 0x20
|
RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS = 0x20
|
||||||
|
RESOURCE_MISC_GENERATE_MIPS = 0x1
|
||||||
|
|
||||||
CREATE_DEVICE_DEBUG = 0x2
|
CREATE_DEVICE_DEBUG = 0x2
|
||||||
|
|
||||||
@@ -1190,6 +1192,16 @@ func (s *IDXGISwapChain) GetBuffer(index int, riid *GUID) (*IUnknown, error) {
|
|||||||
return buf, nil
|
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) {
|
func (c *DeviceContext) Unmap(resource *Resource, subResource uint32) {
|
||||||
syscall.Syscall(
|
syscall.Syscall(
|
||||||
c.Vtbl.Unmap,
|
c.Vtbl.Unmap,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const (
|
|||||||
GREATER = 0x204
|
GREATER = 0x204
|
||||||
GEQUAL = 0x206
|
GEQUAL = 0x206
|
||||||
LINEAR = 0x2601
|
LINEAR = 0x2601
|
||||||
|
LINEAR_MIPMAP_LINEAR = 0x2703
|
||||||
LINK_STATUS = 0x8b82
|
LINK_STATUS = 0x8b82
|
||||||
LUMINANCE = 0x1909
|
LUMINANCE = 0x1909
|
||||||
MAP_READ_BIT = 0x0001
|
MAP_READ_BIT = 0x0001
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ type Functions struct {
|
|||||||
_flush js.Value
|
_flush js.Value
|
||||||
_framebufferRenderbuffer js.Value
|
_framebufferRenderbuffer js.Value
|
||||||
_framebufferTexture2D js.Value
|
_framebufferTexture2D js.Value
|
||||||
|
_generateMipmap js.Value
|
||||||
_getRenderbufferParameteri js.Value
|
_getRenderbufferParameteri js.Value
|
||||||
_getFramebufferAttachmentParameter js.Value
|
_getFramebufferAttachmentParameter js.Value
|
||||||
_getParameter js.Value
|
_getParameter js.Value
|
||||||
@@ -167,6 +168,7 @@ func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
|
|||||||
_flush: _bind(webgl, `flush`),
|
_flush: _bind(webgl, `flush`),
|
||||||
_framebufferRenderbuffer: _bind(webgl, `framebufferRenderbuffer`),
|
_framebufferRenderbuffer: _bind(webgl, `framebufferRenderbuffer`),
|
||||||
_framebufferTexture2D: _bind(webgl, `framebufferTexture2D`),
|
_framebufferTexture2D: _bind(webgl, `framebufferTexture2D`),
|
||||||
|
_generateMipmap: _bind(webgl, `generateMipmap`),
|
||||||
_getRenderbufferParameteri: _bind(webgl, `getRenderbufferParameteri`),
|
_getRenderbufferParameteri: _bind(webgl, `getRenderbufferParameteri`),
|
||||||
_getFramebufferAttachmentParameter: _bind(webgl, `getFramebufferAttachmentParameter`),
|
_getFramebufferAttachmentParameter: _bind(webgl, `getFramebufferAttachmentParameter`),
|
||||||
_getParameter: _bind(webgl, `getParameter`),
|
_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) {
|
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)
|
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 {
|
func (f *Functions) GetError() Enum {
|
||||||
// Avoid slow getError calls. See gio#179.
|
// Avoid slow getError calls. See gio#179.
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ typedef void (*_glFlush)(void);
|
|||||||
typedef void (*_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
|
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 (*_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
|
||||||
typedef void (*_glGenBuffers)(GLsizei n, GLuint *buffers);
|
typedef void (*_glGenBuffers)(GLsizei n, GLuint *buffers);
|
||||||
|
typedef void (*_glGenerateMipmap)(GLenum target);
|
||||||
typedef void (*_glGenFramebuffers)(GLsizei n, GLuint *framebuffers);
|
typedef void (*_glGenFramebuffers)(GLsizei n, GLuint *framebuffers);
|
||||||
typedef void (*_glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers);
|
typedef void (*_glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers);
|
||||||
typedef void (*_glGenTextures)(GLsizei n, GLuint *textures);
|
typedef void (*_glGenTextures)(GLsizei n, GLuint *textures);
|
||||||
@@ -286,6 +287,10 @@ static void glGenBuffers(_glGenBuffers f, GLsizei n, GLuint *buffers) {
|
|||||||
f(n, buffers);
|
f(n, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void glGenerateMipmap(_glGenerateMipmap f, GLenum target) {
|
||||||
|
f(target);
|
||||||
|
}
|
||||||
|
|
||||||
static void glGenFramebuffers(_glGenFramebuffers f, GLsizei n, GLuint *framebuffers) {
|
static void glGenFramebuffers(_glGenFramebuffers f, GLsizei n, GLuint *framebuffers) {
|
||||||
f(n, framebuffers);
|
f(n, framebuffers);
|
||||||
}
|
}
|
||||||
@@ -561,6 +566,7 @@ type Functions struct {
|
|||||||
glFramebufferRenderbuffer C._glFramebufferRenderbuffer
|
glFramebufferRenderbuffer C._glFramebufferRenderbuffer
|
||||||
glFramebufferTexture2D C._glFramebufferTexture2D
|
glFramebufferTexture2D C._glFramebufferTexture2D
|
||||||
glGenBuffers C._glGenBuffers
|
glGenBuffers C._glGenBuffers
|
||||||
|
glGenerateMipmap C._glGenerateMipmap
|
||||||
glGenFramebuffers C._glGenFramebuffers
|
glGenFramebuffers C._glGenFramebuffers
|
||||||
glGenRenderbuffers C._glGenRenderbuffers
|
glGenRenderbuffers C._glGenRenderbuffers
|
||||||
glGenTextures C._glGenTextures
|
glGenTextures C._glGenTextures
|
||||||
@@ -723,6 +729,7 @@ func (f *Functions) load(forceES bool) error {
|
|||||||
f.glFramebufferRenderbuffer = must("glFramebufferRenderbuffer")
|
f.glFramebufferRenderbuffer = must("glFramebufferRenderbuffer")
|
||||||
f.glFramebufferTexture2D = must("glFramebufferTexture2D")
|
f.glFramebufferTexture2D = must("glFramebufferTexture2D")
|
||||||
f.glGenBuffers = must("glGenBuffers")
|
f.glGenBuffers = must("glGenBuffers")
|
||||||
|
f.glGenerateMipmap = must("glGenerateMipmap")
|
||||||
f.glGenFramebuffers = must("glGenFramebuffers")
|
f.glGenFramebuffers = must("glGenFramebuffers")
|
||||||
f.glGenRenderbuffers = must("glGenRenderbuffers")
|
f.glGenRenderbuffers = must("glGenRenderbuffers")
|
||||||
f.glGenTextures = must("glGenTextures")
|
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))
|
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 {
|
func (c *Functions) GetBinding(pname Enum) Object {
|
||||||
return Object{uint(c.GetInteger(pname))}
|
return Object{uint(c.GetInteger(pname))}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ var (
|
|||||||
_glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays")
|
_glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays")
|
||||||
_glCompileShader = LibGLESv2.NewProc("glCompileShader")
|
_glCompileShader = LibGLESv2.NewProc("glCompileShader")
|
||||||
_glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D")
|
_glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D")
|
||||||
|
_glGenerateMipmap = LibGLESv2.NewProc("glGenerateMipmap")
|
||||||
_glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
|
_glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
|
||||||
_glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
|
_glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
|
||||||
_glGenVertexArrays = LibGLESv2.NewProc("glGenVertexArrays")
|
_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) {
|
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)
|
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 {
|
func (c *Functions) CreateBuffer() Buffer {
|
||||||
var buf uintptr
|
var buf uintptr
|
||||||
syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
|
syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
|
||||||
|
|||||||
@@ -303,6 +303,10 @@ static VkResult vkResetDescriptorPool(PFN_vkResetDescriptorPool f, VkDevice devi
|
|||||||
return f(device, descriptorPool, flags);
|
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) {
|
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);
|
f(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
|
||||||
}
|
}
|
||||||
@@ -415,6 +419,7 @@ type (
|
|||||||
Queue = C.VkQueue
|
Queue = C.VkQueue
|
||||||
IndexType = C.VkIndexType
|
IndexType = C.VkIndexType
|
||||||
Image = C.VkImage
|
Image = C.VkImage
|
||||||
|
ImageBlit = C.VkImageBlit
|
||||||
ImageCopy = C.VkImageCopy
|
ImageCopy = C.VkImageCopy
|
||||||
ImageLayout = C.VkImageLayout
|
ImageLayout = C.VkImageLayout
|
||||||
ImageMemoryBarrier = C.VkImageMemoryBarrier
|
ImageMemoryBarrier = C.VkImageMemoryBarrier
|
||||||
@@ -482,9 +487,10 @@ const (
|
|||||||
FORMAT_R32G32B32_SFLOAT Format = C.VK_FORMAT_R32G32B32_SFLOAT
|
FORMAT_R32G32B32_SFLOAT Format = C.VK_FORMAT_R32G32B32_SFLOAT
|
||||||
FORMAT_R32G32B32A32_SFLOAT Format = C.VK_FORMAT_R32G32B32A32_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_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_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_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_SAMPLED_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_SAMPLED_BIT
|
||||||
IMAGE_USAGE_COLOR_ATTACHMENT_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_COLOR_ATTACHMENT_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_GRAPHICS_BIT QueueFlags = C.VK_QUEUE_GRAPHICS_BIT
|
||||||
QUEUE_COMPUTE_BIT QueueFlags = C.VK_QUEUE_COMPUTE_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 (
|
var (
|
||||||
@@ -654,6 +665,7 @@ var funcs struct {
|
|||||||
vkFreeDescriptorSets C.PFN_vkFreeDescriptorSets
|
vkFreeDescriptorSets C.PFN_vkFreeDescriptorSets
|
||||||
vkUpdateDescriptorSets C.PFN_vkUpdateDescriptorSets
|
vkUpdateDescriptorSets C.PFN_vkUpdateDescriptorSets
|
||||||
vkResetDescriptorPool C.PFN_vkResetDescriptorPool
|
vkResetDescriptorPool C.PFN_vkResetDescriptorPool
|
||||||
|
vkCmdBlitImage C.PFN_vkCmdBlitImage
|
||||||
vkCmdCopyImage C.PFN_vkCmdCopyImage
|
vkCmdCopyImage C.PFN_vkCmdCopyImage
|
||||||
vkCreateComputePipelines C.PFN_vkCreateComputePipelines
|
vkCreateComputePipelines C.PFN_vkCreateComputePipelines
|
||||||
vkCreateFence C.PFN_vkCreateFence
|
vkCreateFence C.PFN_vkCreateFence
|
||||||
@@ -794,6 +806,7 @@ func vkInit() error {
|
|||||||
funcs.vkFreeDescriptorSets = must("vkFreeDescriptorSets")
|
funcs.vkFreeDescriptorSets = must("vkFreeDescriptorSets")
|
||||||
funcs.vkUpdateDescriptorSets = must("vkUpdateDescriptorSets")
|
funcs.vkUpdateDescriptorSets = must("vkUpdateDescriptorSets")
|
||||||
funcs.vkResetDescriptorPool = must("vkResetDescriptorPool")
|
funcs.vkResetDescriptorPool = must("vkResetDescriptorPool")
|
||||||
|
funcs.vkCmdBlitImage = must("vkCmdBlitImage")
|
||||||
funcs.vkCmdCopyImage = must("vkCmdCopyImage")
|
funcs.vkCmdCopyImage = must("vkCmdCopyImage")
|
||||||
funcs.vkCreateComputePipelines = must("vkCreateComputePipelines")
|
funcs.vkCreateComputePipelines = must("vkCreateComputePipelines")
|
||||||
funcs.vkCreateFence = must("vkCreateFence")
|
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)
|
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)), ®ions[0], filter)
|
||||||
|
}
|
||||||
|
|
||||||
func CmdCopyImage(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Image, dstLayout ImageLayout, regions []ImageCopy) {
|
func CmdCopyImage(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Image, dstLayout ImageLayout, regions []ImageCopy) {
|
||||||
if len(regions) == 0 {
|
if len(regions) == 0 {
|
||||||
return
|
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))
|
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{
|
inf := C.VkImageCreateInfo{
|
||||||
sType: C.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
sType: C.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||||
imageType: C.VK_IMAGE_TYPE_2D,
|
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),
|
height: C.uint32_t(height),
|
||||||
depth: 1,
|
depth: 1,
|
||||||
},
|
},
|
||||||
mipLevels: 1,
|
mipLevels: C.uint32_t(mipmaps),
|
||||||
arrayLayers: 1,
|
arrayLayers: 1,
|
||||||
samples: C.VK_SAMPLE_COUNT_1_BIT,
|
samples: C.VK_SAMPLE_COUNT_1_BIT,
|
||||||
tiling: C.VK_IMAGE_TILING_OPTIMAL,
|
tiling: C.VK_IMAGE_TILING_OPTIMAL,
|
||||||
@@ -1451,11 +1471,13 @@ func FreeMemory(d Device, mem DeviceMemory) {
|
|||||||
C.vkFreeMemory(funcs.vkFreeMemory, d, mem, nil)
|
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{
|
inf := C.VkSamplerCreateInfo{
|
||||||
sType: C.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
sType: C.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
minFilter: minFilter,
|
minFilter: minFilter,
|
||||||
magFilter: magFilter,
|
magFilter: magFilter,
|
||||||
|
mipmapMode: mipmapMode,
|
||||||
|
maxLod: C.VK_LOD_CLAMP_NONE,
|
||||||
addressModeU: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
addressModeU: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||||
addressModeV: 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{
|
return C.VkImageMemoryBarrier{
|
||||||
sType: C.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
sType: C.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
srcAccessMask: srcMask,
|
srcAccessMask: srcMask,
|
||||||
@@ -1914,9 +1936,10 @@ func BuildImageMemoryBarrier(img Image, srcMask, dstMask AccessFlags, oldLayout,
|
|||||||
newLayout: newLayout,
|
newLayout: newLayout,
|
||||||
image: img,
|
image: img,
|
||||||
subresourceRange: C.VkImageSubresourceRange{
|
subresourceRange: C.VkImageSubresourceRange{
|
||||||
aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
|
aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
levelCount: C.VK_REMAINING_MIP_LEVELS,
|
baseMipLevel: C.uint32_t(baseMip),
|
||||||
layerCount: C.VK_REMAINING_ARRAY_LAYERS,
|
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) {
|
func findMemoryTypeIndex(pd C.VkPhysicalDevice, constraints C.uint32_t, wantProps C.VkMemoryPropertyFlags) (int, bool) {
|
||||||
var memProps C.VkPhysicalDeviceMemoryProperties
|
var memProps C.VkPhysicalDeviceMemoryProperties
|
||||||
C.vkGetPhysicalDeviceMemoryProperties(funcs.vkGetPhysicalDeviceMemoryProperties, pd, &memProps)
|
C.vkGetPhysicalDeviceMemoryProperties(funcs.vkGetPhysicalDeviceMemoryProperties, pd, &memProps)
|
||||||
|
|||||||