<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains the convoluted logic that goes into uploading content into
//! the texture cache's textures.
//!
//! We need to support various combinations of code paths depending on the quirks of
//! each hardware/driver configuration:
//! - direct upload,
//! - staged upload via a pixel buffer object,
//! - staged upload via a direct upload to a staging texture where PBO's aren't supported,
//! - copy from the staging to destination textures, either via blits or batched draw calls.
//!
//! Conceptually a lot of this logic should probably be in the device module, but some code
//! here relies on submitting draw calls via the renderer.


use std::mem;
use std::collections::VecDeque;
use euclid::Transform3D;
use time::precise_time_ns;
use malloc_size_of::MallocSizeOfOps;
use api::units::*;
use api::{ExternalImageSource, PremultipliedColorF, ImageBufferKind, ImageRendering, ImageFormat};
use crate::renderer::{
    Renderer, VertexArrayKind, RendererStats, TextureSampler, TEXTURE_CACHE_DBG_CLEAR_COLOR
};
use crate::internal_types::{
    FastHashMap, TextureUpdateSource, Swizzle, TextureCacheUpdate,
    CacheTextureId, RenderTargetInfo,
};
use crate::device::{
    Device, UploadMethod, Texture, DrawTarget, UploadStagingBuffer, TextureFlags, TextureUploader,
    TextureFilter,
};
use crate::gpu_types::{ZBufferId, CompositeInstance, CompositorTransform};
use crate::batch::BatchTextures;
use crate::texture_pack::{GuillotineAllocator, FreeRectSlice};
use crate::composite::{CompositeFeatures, CompositeSurfaceFormat};
use crate::profiler;
use crate::render_api::MemoryReport;

pub const BATCH_UPLOAD_TEXTURE_SIZE: DeviceIntSize = DeviceIntSize::new(512, 512);

/// Upload a number of items to texture cache textures.
///
/// This is the main entry point of the texture cache upload code.
/// See also the module documentation for more information.
pub fn upload_to_texture_cache(
    renderer: &mut Renderer,
    update_list: FastHashMap<CacheTextureId, Vec<TextureCacheUpdate>>,
) {

    let mut stats = UploadStats {
        num_draw_calls: 0,
        upload_time: 0,
        cpu_buffer_alloc_time: 0,
        texture_alloc_time: 0,
        cpu_copy_time: 0,
        gpu_copy_commands_time: 0,
        bytes_uploaded: 0,
    };

    let upload_total_start = precise_time_ns();

    let mut batch_upload_textures = Vec::new();

    // A list of copies that must be performed from the temporary textures to the texture cache.
    let mut batch_upload_copies = Vec::new();

    // For each texture format, this stores a list of staging buffers
    // and a texture allocator for packing the buffers.
    let mut batch_upload_buffers = FastHashMap::default();

    // For best performance we use a single TextureUploader for all uploads.
    // This allows us to fill PBOs more efficiently and therefore allocate fewer PBOs.
    let mut uploader = renderer.device.upload_texture(
        &mut renderer.texture_upload_pbo_pool,
    );

    let num_updates = update_list.len();

    for (texture_id, updates) in update_list {
        let texture = &renderer.texture_resolver.texture_cache_map[&texture_id].texture;
        for update in updates {
            let TextureCacheUpdate { rect, stride, offset, format_override, source } = update;

            let dummy_data;
            let data = match source {
                TextureUpdateSource::Bytes { ref data } => {
                    &data[offset as usize ..]
                }
                TextureUpdateSource::External { id, channel_index } => {
                    let handler = renderer.external_image_handler
                        .as_mut()
                        .expect("Found external image, but no handler set!");
                    // The filter is only relevant for NativeTexture external images.
                    match handler.lock(id, channel_index, ImageRendering::Auto).source {
                        ExternalImageSource::RawData(data) => {
                            &data[offset as usize ..]
                        }
                        ExternalImageSource::Invalid => {
                            // Create a local buffer to fill the pbo.
                            let bpp = texture.get_format().bytes_per_pixel();
                            let width = stride.unwrap_or(rect.width() * bpp);
                            let total_size = width * rect.height();
                            // WR haven't support RGBAF32 format in texture_cache, so
                            // we use u8 type here.
                            dummy_data = vec![0xFFu8; total_size as usize];
                            &dummy_data
                        }
                        ExternalImageSource::NativeTexture(eid) => {
                            panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id);
                        }
                    }
                }
                TextureUpdateSource::DebugClear => {
                    let draw_target = DrawTarget::from_texture(
                        texture,
                        false,
                    );
                    renderer.device.bind_draw_target(draw_target);
                    renderer.device.clear_target(
                        Some(TEXTURE_CACHE_DBG_CLEAR_COLOR),
                        None,
                        Some(draw_target.to_framebuffer_rect(update.rect.to_i32()))
                    );

                    continue;
                }
            };

            let use_batch_upload = renderer.device.use_batched_texture_uploads() &&
                texture.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE) &&
                rect.width() <= BATCH_UPLOAD_TEXTURE_SIZE.width &&
                rect.height() <= BATCH_UPLOAD_TEXTURE_SIZE.height;

            if use_batch_upload {
                copy_into_staging_buffer(
                    &mut renderer.device,
                    &mut uploader,
                    &mut renderer.staging_texture_pool,
                    rect,
                    stride,
                    data,
                    texture_id,
                    texture,
                    &mut batch_upload_buffers,
                    &mut batch_upload_textures,
                    &mut batch_upload_copies,
                    &mut stats,
                );
            } else {
                let upload_start_time = precise_time_ns();

                stats.bytes_uploaded += uploader.upload(
                    &mut renderer.device,
                    texture,
                    rect,
                    stride,
                    format_override,
                    data.as_ptr(),
                    data.len()
                );<commit_msg>Bug 1729445 - Don't batch texture uploads for large-ish items. r=jnicol<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains the convoluted logic that goes into uploading content into
//! the texture cache's textures.
//!
//! We need to support various combinations of code paths depending on the quirks of
//! each hardware/driver configuration:
//! - direct upload,
//! - staged upload via a pixel buffer object,
//! - staged upload via a direct upload to a staging texture where PBO's aren't supported,
//! - copy from the staging to destination textures, either via blits or batched draw calls.
//!
//! Conceptually a lot of this logic should probably be in the device module, but some code
//! here relies on submitting draw calls via the renderer.


use std::mem;
use std::collections::VecDeque;
use euclid::Transform3D;
use time::precise_time_ns;
use malloc_size_of::MallocSizeOfOps;
use api::units::*;
use api::{ExternalImageSource, PremultipliedColorF, ImageBufferKind, ImageRendering, ImageFormat};
use crate::renderer::{
    Renderer, VertexArrayKind, RendererStats, TextureSampler, TEXTURE_CACHE_DBG_CLEAR_COLOR
};
use crate::internal_types::{
    FastHashMap, TextureUpdateSource, Swizzle, TextureCacheUpdate,
    CacheTextureId, RenderTargetInfo,
};
use crate::device::{
    Device, UploadMethod, Texture, DrawTarget, UploadStagingBuffer, TextureFlags, TextureUploader,
    TextureFilter,
};
use crate::gpu_types::{ZBufferId, CompositeInstance, CompositorTransform};
use crate::batch::BatchTextures;
use crate::texture_pack::{GuillotineAllocator, FreeRectSlice};
use crate::composite::{CompositeFeatures, CompositeSurfaceFormat};
use crate::profiler;
use crate::render_api::MemoryReport;

pub const BATCH_UPLOAD_TEXTURE_SIZE: DeviceIntSize = DeviceIntSize::new(512, 512);

/// Upload a number of items to texture cache textures.
///
/// This is the main entry point of the texture cache upload code.
/// See also the module documentation for more information.
pub fn upload_to_texture_cache(
    renderer: &mut Renderer,
    update_list: FastHashMap<CacheTextureId, Vec<TextureCacheUpdate>>,
) {

    let mut stats = UploadStats {
        num_draw_calls: 0,
        upload_time: 0,
        cpu_buffer_alloc_time: 0,
        texture_alloc_time: 0,
        cpu_copy_time: 0,
        gpu_copy_commands_time: 0,
        bytes_uploaded: 0,
    };

    let upload_total_start = precise_time_ns();

    let mut batch_upload_textures = Vec::new();

    // A list of copies that must be performed from the temporary textures to the texture cache.
    let mut batch_upload_copies = Vec::new();

    // For each texture format, this stores a list of staging buffers
    // and a texture allocator for packing the buffers.
    let mut batch_upload_buffers = FastHashMap::default();

    // For best performance we use a single TextureUploader for all uploads.
    // This allows us to fill PBOs more efficiently and therefore allocate fewer PBOs.
    let mut uploader = renderer.device.upload_texture(
        &mut renderer.texture_upload_pbo_pool,
    );

    let num_updates = update_list.len();

    for (texture_id, updates) in update_list {
        let texture = &renderer.texture_resolver.texture_cache_map[&texture_id].texture;
        for update in updates {
            let TextureCacheUpdate { rect, stride, offset, format_override, source } = update;

            let dummy_data;
            let data = match source {
                TextureUpdateSource::Bytes { ref data } => {
                    &data[offset as usize ..]
                }
                TextureUpdateSource::External { id, channel_index } => {
                    let handler = renderer.external_image_handler
                        .as_mut()
                        .expect("Found external image, but no handler set!");
                    // The filter is only relevant for NativeTexture external images.
                    match handler.lock(id, channel_index, ImageRendering::Auto).source {
                        ExternalImageSource::RawData(data) => {
                            &data[offset as usize ..]
                        }
                        ExternalImageSource::Invalid => {
                            // Create a local buffer to fill the pbo.
                            let bpp = texture.get_format().bytes_per_pixel();
                            let width = stride.unwrap_or(rect.width() * bpp);
                            let total_size = width * rect.height();
                            // WR haven't support RGBAF32 format in texture_cache, so
                            // we use u8 type here.
                            dummy_data = vec![0xFFu8; total_size as usize];
                            &dummy_data
                        }
                        ExternalImageSource::NativeTexture(eid) => {
                            panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id);
                        }
                    }
                }
                TextureUpdateSource::DebugClear => {
                    let draw_target = DrawTarget::from_texture(
                        texture,
                        false,
                    );
                    renderer.device.bind_draw_target(draw_target);
                    renderer.device.clear_target(
                        Some(TEXTURE_CACHE_DBG_CLEAR_COLOR),
                        None,
                        Some(draw_target.to_framebuffer_rect(update.rect.to_i32()))
                    );

                    continue;
                }
            };

            let use_batch_upload = renderer.device.use_batched_texture_uploads() &&
                texture.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE) &&
                rect.width() <= BATCH_UPLOAD_TEXTURE_SIZE.width &&
                rect.height() <= BATCH_UPLOAD_TEXTURE_SIZE.height &&
                rect.area() <= BATCH_UPLOAD_TEXTURE_SIZE.area() / 2;

            if use_batch_upload {
                copy_into_staging_buffer(
                    &mut renderer.device,
                    &mut uploader,
                    &mut renderer.staging_texture_pool,
                    rect,
                    stride,
                    data,
                    texture_id,
                    texture,
                    &mut batch_upload_buffers,
                    &mut batch_upload_textures,
                    &mut batch_upload_copies,
                    &mut stats,
                );
            } else {
                let upload_start_time = precise_time_ns();

                stats.bytes_uploaded += uploader.upload(
                    &mut renderer.device,
                    texture,
                    rect,
                    stride,
                    format_override,
                    data.as_ptr(),
                    data.len()
                );
{'fraction_non_alphanumeric': 0.08233031835339363, 'fraction_numerical': 0.004130759917384801, 'mean_word_length': 1.8698140200286124, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4917178', 'n_tokens_mistral': 3726, 'n_tokens_neox': 3493, 'n_words': 1020}