| // 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 |
| } |