blob: 2ec353fc6a8f056f6e430c8a8c7090a8d3930c8d [file] [log] [blame]
// IsResource returns true if this instance should be considered as a resource.
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vulkan
import (
"bytes"
"fmt"
"android.googlesource.com/platform/tools/gpu/framework/binary/endian"
"android.googlesource.com/platform/tools/gpu/framework/id"
"android.googlesource.com/platform/tools/gpu/framework/log"
"android.googlesource.com/platform/tools/gpu/framework/shadertools"
"android.googlesource.com/platform/tools/gpu/gapid/atom"
"android.googlesource.com/platform/tools/gpu/gapid/builder"
"android.googlesource.com/platform/tools/gpu/gapid/database"
"android.googlesource.com/platform/tools/gpu/gapid/gfxapi"
"android.googlesource.com/platform/tools/gpu/gapid/gfxapi/state"
"android.googlesource.com/platform/tools/gpu/gapid/image"
"android.googlesource.com/platform/tools/gpu/gapid/memory"
"android.googlesource.com/platform/tools/gpu/gapid/messages"
"android.googlesource.com/platform/tools/gpu/gapid/service"
"android.googlesource.com/platform/tools/gpu/gapid/service/path"
)
func (t *ImageObject) IsResource() bool {
// Since there are no good differentiating features for what is a "texture" and what
// image is used for other things, we treat only images that can be used as SAMPLED
// or STORAGE as resources. This may change in the future when we start doing
// replays to get back gpu-generated image data.
is_texture := 0 != (uint32(t.Usage) & uint32(VkImageUsageFlagBits_VK_IMAGE_USAGE_SAMPLED_BIT|
VkImageUsageFlagBits_VK_IMAGE_USAGE_STORAGE_BIT))
return t.Image != 0 && is_texture
}
// ResourceName returns the UI name for the resource.
func (t *ImageObject) ResourceName() string {
return fmt.Sprintf("Image<%d>", t.Image)
}
// ResourceType returns the type of this resource.
func (t *ImageObject) ResourceType() gfxapi.ResourceType {
if uint32(t.Flags)&uint32(VkImageCreateFlagBits_VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0 {
return gfxapi.ResourceType_Cubemap
} else {
return gfxapi.ResourceType_Texture2D
}
}
type unsupportedVulkanFormatError struct {
Format VkFormat
}
func (e *unsupportedVulkanFormatError) Error() string {
return fmt.Sprintf("Unsupported Vulkan format: %d", e.Format)
}
func getImageFormatFromVulkanFormat(vkfmt VkFormat) (image.Format, error) {
switch vkfmt {
case VkFormat_VK_FORMAT_R8G8B8A8_UNORM:
return image.RGBA(), nil
case VkFormat_VK_FORMAT_BC1_RGB_SRGB_BLOCK,
VkFormat_VK_FORMAT_BC1_RGB_UNORM_BLOCK:
return image.S3_DXT1_RGB(), nil
case VkFormat_VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
VkFormat_VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
return image.S3_DXT1_RGBA(), nil
case VkFormat_VK_FORMAT_BC2_UNORM_BLOCK:
return image.S3_DXT3_RGBA(), nil
case VkFormat_VK_FORMAT_BC3_UNORM_BLOCK:
return image.S3_DXT5_RGBA(), nil
case VkFormat_VK_FORMAT_R16G16B16A16_SFLOAT:
return image.RGBAF16(), nil
case VkFormat_VK_FORMAT_R8_UNORM:
return image.Red(), nil
case VkFormat_VK_FORMAT_R16_UNORM:
return image.RedU16(), nil
case VkFormat_VK_FORMAT_R32_SFLOAT:
return image.RedF32(), nil
case VkFormat_VK_FORMAT_R32G32B32A32_SFLOAT:
return image.RGBAF32(), nil
case VkFormat_VK_FORMAT_B8G8R8A8_UNORM:
return image.BGRA(), nil
case VkFormat_VK_FORMAT_D32_SFLOAT_S8_UINT:
return image.DF32S8(), nil
case VkFormat_VK_FORMAT_D24_UNORM_S8_UINT:
return image.D24S8(), nil
case VkFormat_VK_FORMAT_D16_UNORM:
return image.Depth16(), nil
case VkFormat_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
VkFormat_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
return image.ETC2_RGB8(), nil
case VkFormat_VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
VkFormat_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
return image.ETC2_RGBA8_EAC(), nil
default:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
}
}
func getDepthImageFormatFromVulkanFormat(vkfmt VkFormat) (image.Format, error) {
switch vkfmt {
case VkFormat_VK_FORMAT_D32_SFLOAT_S8_UINT:
return image.DepthF32(), nil
case VkFormat_VK_FORMAT_D16_UNORM,
VkFormat_VK_FORMAT_D16_UNORM_S8_UINT:
return image.Depth16(), nil
// TODO: This is wrong, need to convert D24 to D32 with 0x00 in MSB 8-bit
case VkFormat_VK_FORMAT_D24_UNORM_S8_UINT:
fallthrough
default:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
}
}
func setCubemapFace(img *image.Info, cubeMap *gfxapi.CubemapLevel, layerIndex uint32) (success bool) {
if cubeMap == nil || img == nil {
return false
}
switch layerIndex {
case 0:
cubeMap.PositiveX = *img
case 1:
cubeMap.NegativeX = *img
case 2:
cubeMap.PositiveY = *img
case 3:
cubeMap.NegativeY = *img
case 4:
cubeMap.PositiveZ = *img
case 5:
cubeMap.NegativeZ = *img
default:
return false
}
return true
}
// ResourceData returns the resource data given the current state.
func (t *ImageObject) ResourceData(ctx log.Context, s *gfxapi.State, d database.Database, resources gfxapi.ResourceMap) (interface{}, error) {
ctx = ctx.Enter("ImageObject.Resource()")
vkFmt := t.Format
format, err := getImageFormatFromVulkanFormat(vkFmt)
if err != nil {
return nil, &service.ErrDataUnavailable{Reason: messages.ErrNoTextureData(t.ResourceName())}
}
switch t.Type {
case VkImageType_VK_IMAGE_TYPE_2D:
// If this image has VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT set, it should have six layers to
// represent a cubemap
if uint32(t.Flags)&uint32(VkImageCreateFlagBits_VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0 {
// Cubemap
ctx.Error().Log("ResourceData for cubemap")
cubeMapLevels := make([]gfxapi.CubemapLevel, len(t.Layers[0].Levels))
for layerIndex, imageLayer := range t.Layers {
for levelIndex, imageLevel := range imageLayer.Levels {
img := image.Info{
Format: format,
Width: imageLevel.Width,
Height: imageLevel.Height,
Data: &path.Blob{ID: imageLevel.Data.ResourceID(ctx, s, d)},
}
if !setCubemapFace(&img, &cubeMapLevels[levelIndex], layerIndex) {
return nil, &service.ErrDataUnavailable{Reason: messages.ErrNoTextureData(t.ResourceName())}
}
}
}
return &gfxapi.Cubemap{Levels: cubeMapLevels}, nil
} else {
levels := make([]image.Info, len(t.Layers[0].Levels))
for i, level := range t.Layers[0].Levels {
levels[i] = image.Info{
Format: format,
Width: level.Width,
Height: level.Height,
Data: &path.Blob{ID: level.Data.ResourceID(ctx, s, d)},
}
}
return &gfxapi.Texture2D{Levels: levels}, nil
}
default:
return nil, &service.ErrDataUnavailable{Reason: messages.ErrNoTextureData(t.ResourceName())}
}
}
func (t *ImageObject) SetResourceData(ctx log.Context, at *path.Atom,
d database.Database, data interface{}, resources gfxapi.ResourceMap, edits gfxapi.ReplaceCallback) error {
return fmt.Errorf("SetResourceData is not supported for ImageObject")
}
// IsResource returns true if this instance should be considered as a resource.
func (s *ShaderModuleObject) IsResource() bool {
return true
}
// ResourceName returns the UI name for the resource.
func (s *ShaderModuleObject) ResourceName() string {
return fmt.Sprintf("Shader<0x%x>", s.ShaderModule)
}
// ResourceType returns the type of this resource.
func (s *ShaderModuleObject) ResourceType() gfxapi.ResourceType {
return gfxapi.ResourceType_Shader
}
// ResourceData returns the resource data given the current state.
func (s *ShaderModuleObject) ResourceData(ctx log.Context, t *gfxapi.State, d database.Database, resources gfxapi.ResourceMap) (interface{}, error) {
ctx = ctx.Enter("Shader.ResourceData()")
words := s.Words.Read(ctx, nil, t, d, nil)
source := shadertools.DisassembleSpirvBinary(words)
return &gfxapi.Shader{ShaderType: gfxapi.ShaderType_Spirv, Source: source}, nil
}
func (shader *ShaderModuleObject) SetResourceData(ctx log.Context, at *path.Atom,
d database.Database, data interface{}, resources gfxapi.ResourceMap, edits gfxapi.ReplaceCallback) error {
ctx = ctx.Enter("ShaderModuleObject.SetResourceData()")
// Dirty. TODO: Make separate type for getting info for a single resource.
pathCapture := at.Atoms.Capture
resourceBundles, err := database.Build(ctx, &builder.GetResourceBundles{Capture: pathCapture}, d)
if err != nil {
return err
}
bundles := resourceBundles.(*service.ResourceBundles).Bundles
resourceID := resources[shader]
var info service.ResourceInfo
for _, b := range bundles {
if b.Type == shader.ResourceType() {
for _, ri := range b.Resources {
if ri.ID == resourceID {
info = ri
break
}
}
break
}
}
capture, err := service.ResolveCapture(ctx, pathCapture.ID, d)
if err != nil {
return err
}
res, err := database.Resolve(ctx, id.ID(capture.Atoms), d)
if err != nil {
return err
}
atoms := res.(*atom.List).Atoms
index := len(info.Accesses) - 1
for info.Accesses[index] > at.Index && index >= 0 {
index--
}
for j := index; j >= 0; j-- {
i := info.Accesses[j]
if a, ok := atoms[i].(*VkCreateShaderModule); ok {
edits(uint64(i), a.Replace(ctx, data))
return nil
}
}
return fmt.Errorf("No atom to set data in")
}
func (a *VkCreateShaderModule) Replace(ctx log.Context, data interface{}) gfxapi.ResourceAtom {
ctx = ctx.Enter("VkCreateShaderModule.Replace()")
d := database.Get(ctx)
state := state.New(ctx)
a.Mutate(ctx, state, d, nil)
shader := data.(*gfxapi.Shader)
codeSlice := shadertools.AssembleSpirvText(shader.Source)
if codeSlice == nil {
return nil
}
code := atom.Must(atom.AllocData(ctx, state, d, codeSlice))
device := a.Device
pAlloc := memory.Pointer(a.PAllocator)
pShaderModule := memory.Pointer(a.PShaderModule)
result := a.Result
createInfo := a.PCreateInfo.Read(ctx, a, state, d, nil)
createInfo.PCode = U32ᶜᵖ(code.Ptr())
createInfo.CodeSize = uint64(len(codeSlice)) * 4
// TODO(qining): The following is a hack to work around memory.Write().
// In VkShaderModuleCreateInfo, CodeSize should be of type 'size', but
// 'uint64' is used for now, and memory.Write() will always treat is as
// a 8-byte type and causing padding issues so that won't encode the struct
// correctly.
// Possible solution: define another type 'size' and handle it correctly in
// memory.Write().
buf := &bytes.Buffer{}
writer := endian.Writer(buf, state.MemoryLayout.Endian)
VkShaderModuleCreateInfoEncodeRaw(state, writer, &createInfo)
newCreateInfo := atom.Must(atom.AllocData(ctx, state, d, buf.Bytes()))
newAtom := NewVkCreateShaderModule(device, newCreateInfo.Ptr(), pAlloc, pShaderModule, result)
// Carry all non-observation extras through.
for _, e := range a.Extras().All() {
if _, ok := e.(*atom.Observations); !ok {
newAtom.Extras().Add(e)
}
}
// Add observations
newAtom.AddRead(newCreateInfo.Data()).AddRead(code.Data())
for _, w := range a.Extras().Observations().Writes {
newAtom.AddWrite(w.Range, w.ID)
}
return newAtom
}