| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "software_view.h" |
| |
| #include <lib/trace/event.h> |
| #include <lib/ui/scenic/cpp/commands.h> |
| |
| #include <algorithm> |
| |
| #include <fbl/algorithm.h> |
| |
| namespace frame_compression { |
| namespace { |
| |
| // sRGB color space. |
| constexpr uint32_t kColor0 = 0xff6448fe; |
| constexpr uint32_t kColor1 = 0xffb3d5eb; |
| |
| // Inspect values. |
| constexpr char kView[] = "view"; |
| constexpr char kModifier[] = "modifier"; |
| constexpr char kImage[] = "image"; |
| constexpr char kImageBytes[] = "image_bytes"; |
| constexpr char kImageBytesUsed[] = "image_bytes_used"; |
| constexpr char kImageBytesDeduped[] = "image_bytes_deduped"; |
| constexpr char kWidthInTiles[] = "width_in_tiles"; |
| constexpr char kHeightInTiles[] = "height_in_tiles"; |
| |
| } // namespace |
| |
| SoftwareView::SoftwareView(scenic::ViewContext context, uint64_t modifier, uint32_t width, |
| uint32_t height, uint32_t paint_count, FILE* png_fp, |
| inspect::Node inspect_node) |
| : BaseView(std::move(context), "Software View Example", width, height, std::move(inspect_node)), |
| modifier_(modifier), |
| paint_count_(paint_count), |
| png_fp_(png_fp), |
| inspect_node_(top_inspect_node_.CreateLazyValues(kView, [this] { return PopulateStats(); })) { |
| zx_status_t status = component_context()->svc()->Connect(sysmem_allocator_.NewRequest()); |
| FX_CHECK(status == ZX_OK); |
| |
| fuchsia::sysmem::BufferCollectionTokenSyncPtr local_token; |
| status = sysmem_allocator_->AllocateSharedCollection(local_token.NewRequest()); |
| FX_CHECK(status == ZX_OK); |
| fuchsia::sysmem::BufferCollectionTokenSyncPtr scenic_token; |
| status = local_token->Duplicate(std::numeric_limits<uint32_t>::max(), scenic_token.NewRequest()); |
| FX_CHECK(status == ZX_OK); |
| status = local_token->Sync(); |
| FX_CHECK(status == ZX_OK); |
| |
| const uint32_t kBufferId = 1; |
| session()->RegisterBufferCollection(kBufferId, std::move(scenic_token)); |
| |
| fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection; |
| status = sysmem_allocator_->BindSharedCollection(std::move(local_token), |
| buffer_collection.NewRequest()); |
| FX_CHECK(status == ZX_OK); |
| |
| // |
| // Set buffer collection constraints for CPU usage. |
| // |
| |
| fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.min_buffer_count = kNumImages; |
| constraints.usage.cpu = fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints.min_size_bytes = 0; |
| constraints.buffer_memory_constraints.max_size_bytes = 0xffffffff; |
| constraints.buffer_memory_constraints.physically_contiguous_required = false; |
| constraints.buffer_memory_constraints.secure_required = false; |
| constraints.buffer_memory_constraints.ram_domain_supported = true; |
| constraints.buffer_memory_constraints.cpu_domain_supported = true; |
| constraints.buffer_memory_constraints.inaccessible_domain_supported = false; |
| constraints.buffer_memory_constraints.heap_permitted_count = 0; |
| constraints.image_format_constraints_count = 1; |
| fuchsia::sysmem::ImageFormatConstraints& image_constraints = |
| constraints.image_format_constraints[0]; |
| image_constraints = fuchsia::sysmem::ImageFormatConstraints(); |
| image_constraints.min_coded_width = width_; |
| image_constraints.min_coded_height = height_; |
| image_constraints.max_coded_width = width_; |
| image_constraints.max_coded_height = height_; |
| image_constraints.min_bytes_per_row = 0; |
| image_constraints.max_bytes_per_row = std::numeric_limits<uint32_t>::max(); |
| image_constraints.max_coded_width_times_coded_height = std::numeric_limits<uint32_t>::max(); |
| image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::R8G8B8A8; |
| image_constraints.color_spaces_count = 1; |
| image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB; |
| image_constraints.pixel_format.has_format_modifier = true; |
| image_constraints.pixel_format.format_modifier.value = modifier_; |
| |
| // Force bytes per row to 4 * |width_| when using linear buffer. |
| if (modifier_ == fuchsia::sysmem::FORMAT_MODIFIER_LINEAR) { |
| image_constraints.min_bytes_per_row = width_ * 4; |
| image_constraints.max_bytes_per_row = width_ * 4; |
| } |
| |
| status = buffer_collection->SetConstraints(true, constraints); |
| FX_CHECK(status == ZX_OK); |
| |
| zx_status_t allocation_status = ZX_OK; |
| fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info = {}; |
| status = buffer_collection->WaitForBuffersAllocated(&allocation_status, &buffer_collection_info); |
| FX_CHECK(status == ZX_OK); |
| FX_CHECK(allocation_status == ZX_OK); |
| FX_CHECK(buffer_collection_info.settings.image_format_constraints.pixel_format.type == |
| image_constraints.pixel_format.type); |
| bool needs_flush = buffer_collection_info.settings.buffer_settings.coherency_domain == |
| fuchsia::sysmem::CoherencyDomain::RAM; |
| uint32_t stride = buffer_collection_info.settings.image_format_constraints.min_bytes_per_row; |
| |
| // |
| // Initialize images from allocated buffer collection. |
| // |
| |
| for (uint32_t i = 0; i < kNumImages; ++i) { |
| auto& image = images_[i]; |
| |
| image.image_id = session()->AllocResourceId(); |
| fuchsia::sysmem::ImageFormat_2 image_format = {}; |
| image_format.coded_width = width_; |
| image_format.coded_height = height_; |
| session()->Enqueue(scenic::NewCreateImage2Cmd(image.image_id, width_, height_, kBufferId, i)); |
| |
| uint8_t* vmo_base; |
| FX_CHECK(buffer_collection_info.buffers[i].vmo != ZX_HANDLE_INVALID); |
| const zx::vmo& image_vmo = buffer_collection_info.buffers[i].vmo; |
| auto image_vmo_bytes = buffer_collection_info.settings.buffer_settings.size_bytes; |
| FX_CHECK(image_vmo_bytes > 0); |
| status = zx::vmar::root_self()->map(ZX_VM_PERM_WRITE | ZX_VM_PERM_READ, 0, image_vmo, 0, |
| image_vmo_bytes, reinterpret_cast<uintptr_t*>(&vmo_base)); |
| vmo_base += buffer_collection_info.buffers[i].vmo_usable_start; |
| |
| image.vmo_ptr = vmo_base; |
| image.image_bytes = image_vmo_bytes; |
| switch (modifier) { |
| case fuchsia::sysmem::FORMAT_MODIFIER_ARM_AFBC_16X16_YUV_TILED_HEADER: { |
| uint32_t width_in_tiles = |
| fbl::round_up(width_, kTiledAfbcWidthAlignment) / kAfbcTilePixelWidth; |
| uint32_t height_in_tiles = |
| fbl::round_up(height_, kTiledAfbcHeightAlignment) / kAfbcTilePixelHeight; |
| image.width_in_tiles = width_in_tiles; |
| image.height_in_tiles = height_in_tiles; |
| } break; |
| case fuchsia::sysmem::FORMAT_MODIFIER_LINEAR: |
| image.stride = stride; |
| break; |
| default: |
| FX_NOTREACHED() << "Modifier not supported."; |
| } |
| image.needs_flush = needs_flush; |
| image.inspect_node = top_inspect_node_.CreateLazyNode(kImage + std::to_string(i), [this, i] { |
| auto& image = images_[i]; |
| return PopulateImageStats(image); |
| }); |
| } |
| |
| buffer_collection->Close(); |
| } |
| |
| void SoftwareView::OnSceneInvalidated(fuchsia::images::PresentationInfo presentation_info) { |
| if (!has_logical_size()) { |
| return; |
| } |
| |
| uint32_t frame_number = GetNextFrameNumber(); |
| if (frame_number < paint_count_) { |
| auto& image = images_[GetNextImageIndex()]; |
| if (png_fp_) { |
| png_infop info_ptr; |
| auto png = CreatePngReadStruct(png_fp_, &info_ptr); |
| SetPixelsFromPng(image, png); |
| DestroyPngReadStruct(png, info_ptr); |
| } else { |
| SetPixelsFromColorOffset(image, GetNextColorOffset()); |
| } |
| material_.SetTexture(image.image_id); |
| } |
| |
| Animate(presentation_info); |
| |
| // The rectangle is constantly animating; invoke InvalidateScene() to guarantee |
| // that OnSceneInvalidated() will be called again. |
| InvalidateScene(); |
| } |
| |
| void SoftwareView::SetPixelsFromColorOffset(Image& image, uint32_t color_offset) { |
| switch (modifier_) { |
| case fuchsia::sysmem::FORMAT_MODIFIER_ARM_AFBC_16X16_YUV_TILED_HEADER: |
| SetAfbcPixelsFromColorOffset(image, color_offset); |
| break; |
| case fuchsia::sysmem::FORMAT_MODIFIER_LINEAR: |
| SetLinearPixelsFromColorOffset(image, color_offset); |
| break; |
| default: |
| FX_NOTREACHED() << "Modifier not supported."; |
| } |
| image.image_bytes_used = image.image_bytes; |
| } |
| |
| void SoftwareView::SetAfbcPixelsFromColorOffset(Image& image, uint32_t color_offset) { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromColorOffset"); |
| |
| uint32_t width_in_tiles = image.width_in_tiles; |
| uint32_t height_in_tiles = image.height_in_tiles; |
| uint32_t tile_count = width_in_tiles * height_in_tiles; |
| uint32_t body_offset = |
| fbl::round_up(tile_count * kAfbcBytesPerBlockHeader, kTiledAfbcBodyAlignment); |
| uint32_t subtile_num_bytes = kTileNumBytes / (kAfbcSubtileSize * kAfbcSubtileSize); |
| uint32_t subtile_stride = subtile_num_bytes / kAfbcSubtileSize; |
| uint32_t width_in_superblocks = width_in_tiles / kAfbcHeaderTileWidth; |
| uint32_t height_in_superblocks = height_in_tiles / kAfbcHeaderTileHeight; |
| |
| uint8_t* header_base = image.vmo_ptr; |
| uint8_t* body_base = header_base + body_offset; |
| |
| uint32_t next_tile_index = 0; |
| for (unsigned k = 0; k < height_in_superblocks; k++) { |
| for (unsigned j = 0; j < width_in_superblocks; j++) { |
| for (unsigned i = 0; i < kAfbcHeaderTileBlocks; i++) { |
| // 8x8 superblock layout: |
| // |
| // +--+--+--+--+--+--+--+-- |
| // |01|02|05|06|17|18|21|.. |
| // +--+--+--+--+--+--+--+ |
| // |03|04|07|08|19|20|.. |
| // +--+--+--+--+--+--+ |
| // |09|10|13|14|25|.. |
| // +--+--+--+--+--+ |
| // |11|12|15|16|.. |
| // +--+--+--+--+ |
| // |32|33|36|.. |
| // +--+--+--+ |
| // |34|35|.. |
| // +--+--+ |
| // |40|.. |
| // +--+ |
| // +.. |
| // |
| |
| // 4x4 offset and index: |
| unsigned block_4x4 = i / 16; |
| unsigned block_4x4_index = i % 16; |
| |
| // 2x2 offset and index: |
| unsigned block_2x2 = block_4x4_index / 4; |
| unsigned block_2x2_index = block_4x4_index % 4; |
| |
| // y offsets: |
| unsigned block_4x4_y = block_4x4 / 2; |
| unsigned block_2x2_y = block_2x2 / 2; |
| unsigned block_y = block_2x2_index / 2; |
| |
| unsigned tile_y = |
| ((k * kAfbcHeaderTileHeight) + block_4x4_y * 4 + block_2x2_y * 2 + block_y) * |
| kAfbcTilePixelHeight; |
| unsigned tile_y_end = tile_y + kAfbcTilePixelHeight; |
| |
| unsigned header_offset = (((k * width_in_superblocks + j) * kAfbcHeaderTileBlocks) + i) * |
| kAfbcBytesPerBlockHeader; |
| uint8_t* header_ptr = header_base + header_offset; |
| // Use solid color tile if possible. |
| if (tile_y >= color_offset || tile_y_end < color_offset) { |
| uint32_t color = tile_y >= color_offset ? kColor0 : kColor1; |
| // Reset header. |
| header_ptr[0] = header_ptr[1] = header_ptr[2] = header_ptr[3] = header_ptr[4] = |
| header_ptr[5] = header_ptr[6] = header_ptr[7] = header_ptr[12] = header_ptr[13] = |
| header_ptr[14] = header_ptr[15] = 0; |
| // Solid colors are stored at offset 8 in block header. |
| *(reinterpret_cast<uint32_t*>(header_ptr + 8)) = color; |
| } else { |
| uint32_t tile_index = next_tile_index++; |
| uint32_t tile_offset = kTileNumBytes * tile_index; |
| // 16 sub-tiles. |
| constexpr struct { |
| unsigned x; |
| unsigned y; |
| } kSubtileOffset[kAfbcSubtileSize * kAfbcSubtileSize] = { |
| {4, 4}, {0, 4}, {0, 0}, {4, 0}, {8, 0}, {12, 0}, {12, 4}, {8, 4}, |
| {8, 8}, {12, 8}, {12, 12}, {8, 12}, {4, 12}, {0, 12}, {0, 8}, {4, 8}, |
| }; |
| |
| for (unsigned l = 0; l < countof(kSubtileOffset); ++l) { |
| unsigned offset = tile_offset + subtile_num_bytes * l; |
| |
| for (unsigned yy = 0; yy < kAfbcSubtileSize; ++yy) { |
| unsigned y = tile_y + kSubtileOffset[l].y + yy; |
| uint32_t color = y >= color_offset ? kColor0 : kColor1; |
| uint32_t* target = |
| reinterpret_cast<uint32_t*>(body_base + offset + yy * subtile_stride); |
| |
| for (unsigned xx = 0; xx < kAfbcSubtileSize; ++xx) { |
| target[xx] = color; |
| } |
| } |
| } |
| |
| if (image.needs_flush) { |
| zx_cache_flush(body_base + tile_offset, kTileNumBytes, ZX_CACHE_FLUSH_DATA); |
| } |
| |
| // Store offset of uncompressed tile memory in byte 0-3. |
| *(reinterpret_cast<uint32_t*>(header_ptr)) = body_offset + tile_offset; |
| |
| // Set byte 4-15 to disable compression for tile memory. |
| header_ptr[4] = header_ptr[7] = header_ptr[10] = header_ptr[13] = 0x41; |
| header_ptr[5] = header_ptr[8] = header_ptr[11] = header_ptr[14] = 0x10; |
| header_ptr[6] = header_ptr[9] = header_ptr[12] = header_ptr[15] = 0x04; |
| } |
| } |
| } |
| } |
| |
| if (image.needs_flush) { |
| zx_cache_flush(header_base, body_offset, ZX_CACHE_FLUSH_DATA); |
| } |
| |
| image.image_bytes_used = body_offset + next_tile_index * kTileNumBytes; |
| image.image_bytes_deduped = 0; |
| } |
| |
| void SoftwareView::SetLinearPixelsFromColorOffset(Image& image, uint32_t color_offset) { |
| TRACE_DURATION("gfx", "SoftwareView::SetLinearPixelsFromColorOffset"); |
| |
| uint8_t* vmo_base = image.vmo_ptr; |
| for (uint32_t y = 0; y < height_; ++y) { |
| uint32_t color = y >= color_offset ? kColor0 : kColor1; |
| uint32_t* target = reinterpret_cast<uint32_t*>(&vmo_base[y * image.stride]); |
| for (uint32_t x = 0; x < width_; ++x) { |
| target[x] = color; |
| } |
| } |
| |
| if (image.needs_flush) { |
| zx_cache_flush(image.vmo_ptr, image.image_bytes, ZX_CACHE_FLUSH_DATA); |
| } |
| } |
| |
| void SoftwareView::SetPixelsFromPng(Image& image, png_structp png) { |
| switch (modifier_) { |
| case fuchsia::sysmem::FORMAT_MODIFIER_ARM_AFBC_16X16_YUV_TILED_HEADER: |
| SetAfbcPixelsFromPng(image, png); |
| break; |
| case fuchsia::sysmem::FORMAT_MODIFIER_LINEAR: |
| SetLinearPixelsFromPng(image, png); |
| break; |
| default: |
| FX_NOTREACHED() << "Modifier not supported."; |
| } |
| } |
| |
| void SoftwareView::SetAfbcPixelsFromPng(Image& image, png_structp png) { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromPng"); |
| |
| uint32_t width_in_tiles = image.width_in_tiles; |
| uint32_t height_in_tiles = image.height_in_tiles; |
| uint32_t tile_count = width_in_tiles * height_in_tiles; |
| uint32_t body_offset = |
| fbl::round_up(tile_count * kAfbcBytesPerBlockHeader, kTiledAfbcBodyAlignment); |
| uint32_t subtile_num_bytes = kTileNumBytes / (kAfbcSubtileSize * kAfbcSubtileSize); |
| uint32_t subtile_stride = subtile_num_bytes / kAfbcSubtileSize; |
| uint32_t width_in_superblocks = width_in_tiles / kAfbcHeaderTileWidth; |
| uint32_t stride = width_in_tiles * kAfbcTilePixelWidth; |
| |
| uint8_t* header_base = image.vmo_ptr; |
| uint8_t* body_base = header_base + body_offset; |
| |
| uint32_t next_tile_index = 0; |
| uint32_t solid_tile_count = 0; |
| |
| // Resize scratch buffer to fit one row of tiles. |
| scratch_.resize(stride * kAfbcTilePixelHeight); |
| // Reset tile map. |
| image.tiles.clear(); |
| |
| uint32_t rows_left = height_; |
| for (unsigned j = 0; j < height_in_tiles; ++j) { |
| row_pointers_.clear(); |
| for (uint32_t y = 0; y < kAfbcTilePixelHeight; ++y) { |
| row_pointers_.push_back(reinterpret_cast<png_bytep>(scratch_.data()) + y * stride * 4); |
| } |
| memset(scratch_.data(), 0, scratch_.size() * 4); |
| |
| if (rows_left) { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromPng::ReadRows"); |
| uint32_t rows = std::min(rows_left, static_cast<uint32_t>(row_pointers_.size())); |
| png_read_rows(png, row_pointers_.data(), nullptr, rows); |
| rows_left -= rows; |
| } |
| |
| for (unsigned i = 0; i < width_in_tiles; i++) { |
| unsigned tile_x = i * kAfbcTilePixelWidth; |
| |
| constexpr uint32_t kAfbcSubtileNumPixels = kAfbcSubtileSize * kAfbcSubtileSize; |
| |
| #define S(offset) ((offset)*kAfbcSubtileNumPixels) |
| constexpr uint32_t kAfbcSubtileOffset[4][4] = { |
| {S(2), S(1), S(14), S(13)}, |
| {S(3), S(0), S(15), S(12)}, |
| {S(4), S(7), S(8), S(11)}, |
| {S(5), S(6), S(9), S(10)}, |
| }; |
| #undef S |
| |
| uint32_t tile_pixels[kTileNumPixels]; |
| uint32_t last_pixel = scratch_[tile_x]; |
| bool is_solid_color = true; |
| |
| { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromPng::LinearToTile"); |
| |
| // Convert to uncompressed tile memory and detect solid colors in the process. |
| for (unsigned y = 0; y < kAfbcTilePixelHeight; ++y) { |
| uint32_t* row = scratch_.data() + y * stride + tile_x; |
| uint32_t subtile_j = y / kAfbcSubtileSize; |
| uint32_t subtile_y = y % kAfbcSubtileSize; |
| uint32_t subtile_row_offset = subtile_y * kAfbcSubtileSize; |
| for (unsigned x = 0; x < kAfbcTilePixelWidth; ++x) { |
| uint32_t pixel = row[x]; |
| uint32_t subtile_i = x / kAfbcSubtileSize; |
| uint32_t subtile_x = x % kAfbcSubtileSize; |
| uint32_t tile_offset = |
| kAfbcSubtileOffset[subtile_i][subtile_j] + subtile_row_offset + subtile_x; |
| tile_pixels[tile_offset] = pixel; |
| is_solid_color = is_solid_color && (pixel == last_pixel); |
| last_pixel = pixel; |
| } |
| } |
| } |
| |
| unsigned superblock_i = i / kAfbcHeaderTileWidth; |
| unsigned superblock_x = i % kAfbcHeaderTileWidth; |
| unsigned block_4x4_i = superblock_x / 4; |
| unsigned block_4x4_x = superblock_x % 4; |
| unsigned block_2x2_i = block_4x4_x / 2; |
| unsigned block_2x2_x = block_4x4_x % 2; |
| |
| unsigned superblock_j = j / kAfbcHeaderTileHeight; |
| unsigned superblock_y = j % kAfbcHeaderTileHeight; |
| unsigned block_4x4_j = superblock_y / 4; |
| unsigned block_4x4_y = superblock_y % 4; |
| unsigned block_2x2_j = block_4x4_y / 2; |
| unsigned block_2x2_y = block_4x4_y % 2; |
| |
| unsigned superblock_idx = superblock_j * width_in_superblocks + superblock_i; |
| |
| unsigned tile_idx = superblock_idx * kAfbcHeaderTileBlocks; |
| tile_idx += (block_4x4_j * 2 + block_4x4_i) * 16; |
| tile_idx += (block_2x2_j * 2 + block_2x2_i) * 4; |
| tile_idx += block_2x2_y * 2 + block_2x2_x; |
| |
| unsigned header_offset = tile_idx * kAfbcBytesPerBlockHeader; |
| uint8_t* header_ptr = header_base + header_offset; |
| if (is_solid_color) { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromPng::SetSolid"); |
| |
| // Reset header. |
| header_ptr[0] = header_ptr[1] = header_ptr[2] = header_ptr[3] = header_ptr[4] = |
| header_ptr[5] = header_ptr[6] = header_ptr[7] = header_ptr[12] = header_ptr[13] = |
| header_ptr[14] = header_ptr[15] = 0; |
| |
| // Solid colors are stored at offset 8 in block header. |
| *(reinterpret_cast<uint32_t*>(header_ptr + 8)) = last_pixel; |
| ++solid_tile_count; |
| } else { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromPng::Uncompressed"); |
| |
| uint32_t tile_offset; |
| |
| auto it = image.tiles.find((Tile){tile_pixels}); |
| if (it == image.tiles.end()) { |
| uint32_t tile_index = next_tile_index++; |
| tile_offset = tile_index * kTileNumBytes; |
| |
| // Copy uncompressed tile memory and add new unique tile. |
| memcpy(body_base + tile_offset, tile_pixels, kTileNumBytes); |
| if (image.needs_flush) { |
| zx_cache_flush(body_base + tile_offset, kTileNumBytes, ZX_CACHE_FLUSH_DATA); |
| } |
| image.tiles.insert( |
| {(Tile){reinterpret_cast<uint32_t*>(body_base + tile_offset)}, tile_offset}); |
| } else { |
| tile_offset = it->second; |
| } |
| |
| // Store offset of uncompressed tile memory in byte 0-3. |
| *(reinterpret_cast<uint32_t*>(header_ptr)) = body_offset + tile_offset; |
| |
| // Set byte 4-15 to disable compression for tile memory. |
| header_ptr[4] = header_ptr[7] = header_ptr[10] = header_ptr[13] = 0x41; |
| header_ptr[5] = header_ptr[8] = header_ptr[11] = header_ptr[14] = 0x10; |
| header_ptr[6] = header_ptr[9] = header_ptr[12] = header_ptr[15] = 0x04; |
| } |
| } |
| } |
| |
| if (image.needs_flush) { |
| TRACE_DURATION("gfx", "SoftwareView::SetAfbcPixelsFromPng::Flush"); |
| zx_cache_flush(header_base, body_offset, ZX_CACHE_FLUSH_DATA); |
| } |
| |
| image.image_bytes_used = body_offset + next_tile_index * kTileNumBytes; |
| image.image_bytes_deduped = ((tile_count - solid_tile_count) - next_tile_index) * kTileNumBytes; |
| } |
| |
| void SoftwareView::SetLinearPixelsFromPng(Image& image, png_structp png) { |
| TRACE_DURATION("gfx", "SoftwareView::SetLinearPixelsFromPng"); |
| |
| row_pointers_.clear(); |
| uint8_t* vmo_base = image.vmo_ptr; |
| for (uint32_t y = 0; y < height_; ++y) { |
| row_pointers_.push_back(reinterpret_cast<png_bytep>(&vmo_base[y * image.stride])); |
| } |
| |
| { |
| TRACE_DURATION("gfx", "SoftwareView::SetLinearPixelsFromPng::ReadImage"); |
| png_read_image(png, row_pointers_.data()); |
| } |
| |
| if (image.needs_flush) { |
| TRACE_DURATION("gfx", "SoftwareView::SetLinearPixelsFromPng::Flush"); |
| zx_cache_flush(image.vmo_ptr, image.image_bytes, ZX_CACHE_FLUSH_DATA); |
| } |
| |
| image.image_bytes_used = height_ * image.stride; |
| image.image_bytes_deduped = 0; |
| } |
| |
| fit::promise<inspect::Inspector> SoftwareView::PopulateStats() const { |
| inspect::Inspector inspector; |
| |
| inspector.GetRoot().CreateUint(kModifier, modifier_, &inspector); |
| |
| return fit::make_ok_promise(std::move(inspector)); |
| } |
| |
| fit::promise<inspect::Inspector> SoftwareView::PopulateImageStats(const Image& image) const { |
| inspect::Inspector inspector; |
| |
| inspector.GetRoot().CreateUint(kImageBytes, image.image_bytes, &inspector); |
| inspector.GetRoot().CreateUint(kImageBytesUsed, image.image_bytes_used, &inspector); |
| inspector.GetRoot().CreateUint(kImageBytesDeduped, image.image_bytes_deduped, &inspector); |
| if (modifier_ == fuchsia::sysmem::FORMAT_MODIFIER_ARM_AFBC_16X16_YUV_TILED_HEADER) { |
| inspector.GetRoot().CreateUint(kWidthInTiles, image.width_in_tiles, &inspector); |
| inspector.GetRoot().CreateUint(kHeightInTiles, image.height_in_tiles, &inspector); |
| } |
| |
| return fit::make_ok_promise(std::move(inspector)); |
| } |
| |
| } // namespace frame_compression |