public void Bind_Compute_Resources(Texture target_image, Compute_Resources compute_resources, Evolution_Settings evolution_settings)   // binds all the buffers and textures and the uniforms that dont change in runtime to the relevant compute shader
    {
        bind_population_pool_buffer(compute_resources.population_pool_buffer);
        bind_second_gen_buffer(compute_resources.second_gen_population_pool_buffer);
        bind_per_pixel_fitness_buffer(compute_resources.per_pixel_fitnes_buffer);
        bind_rows_sum_buffer(compute_resources.per_row_sum_buffer);
        bind_population_pool_fitness_buffer(compute_resources.population_pool_fitness_buffer);
        bind_population_accumlative_probablities_buffer(compute_resources.population_accumlative_prob_buffer);
        bind_second_gen_parent_ids_buffer(compute_resources.second_gen_parents_ids_buffer);
        bind_fittest_member_buffer(compute_resources.fittest_member_buffer);
        bind_position_domain_buffer(compute_resources.position_domain_buffer);
        bind_positon_domain_arguments_buffer(compute_resources.positon_domain_arguments_buffer);

        bind_original_texture(target_image);
        bind_forged_texture(compute_resources.compute_forged_in_render_texture);
        bind_orignal_gradient_texture(compute_resources.original_image_gradient);
        bind_sobel_out(compute_resources.sobel_out);
        bind_original_blured(compute_resources.original_image_blured);
        bind_gaussian_out(compute_resources.gaussian_out);
        bind_debug_texture(compute_resources.debug_texture);

        set_image_dimensions((uint)target_image.width, (uint)target_image.height);
        set_evolution_settings(evolution_settings.populationPoolNumber, evolution_settings.maximumNumberOfBrushStrokes);
    }
Esempio n. 2
0
    // ----------------------------------------------------------------------------------------------------------------------------------------------------------------
    // Initialisation
    // ----------------------------------------------------------------------------------------------------------------------------------------------------------------

    public void initialise_stage(Texture original_image, RenderTexture clear_with_base, Compute_Shaders shaders, bool is_black_white, uint stage_id, EvolutionManager evl_man_lazy_ref)
    {
        fitness_data = new FitnessData();

        // ____________________________________________________________________________________________________
        // Initializing Parameter passed from Evolution Manager

        ImageToReproduce = original_image;
        compute_shaders  = shaders;
        scale_stage_id   = stage_id;

        // ____________________________________________________________________________________________________
        // Compute Resources Initialization
        compute_resources = new Compute_Resources();
        compute_resources.Consruct_Buffers(ImageToReproduce, clear_with_base, evolution_settings);

        // ____________________________________________________________________________________________________
        // Materials

        rendering_material = new Material(Shader.Find("Unlit/PopulationShader"));
        if (!rendering_material)
        {
            Debug.LogError("Couldnt find the population shader");
        }

        rendering_material.SetTexture("_MainTex", evolution_settings.brushTexture);

        fittest_rendering_material = new Material(Shader.Find("Unlit/FittestRenderShader"));
        if (!fittest_rendering_material)
        {
            Debug.LogError("could not find the sahder for rendering the fittest population member");
        }

        fittest_rendering_material.SetInt("_genes_number_per_member", (int)evolution_settings.maximumNumberOfBrushStrokes);
        fittest_rendering_material.SetTexture("_MainTex", evolution_settings.brushTexture);

        rendering_material.SetBuffer("_population_pool", compute_resources.population_pool_buffer);
        rendering_material.SetBuffer("_population_pool", compute_resources.population_pool_buffer);
        fittest_rendering_material.SetBuffer("_population_pool", compute_resources.population_pool_buffer);
        fittest_rendering_material.SetBuffer("_fittest_member", compute_resources.fittest_member_buffer);
        // ____________________________________________________________________________________________________
        // Compute Shader Bindings


        compute_shaders.Bind_Compute_Resources(ImageToReproduce, compute_resources, evolution_settings);

        UpdateBalancingParameters(0); // so that the parameters for the sobel/ gaussian and position search domain is set here

        // ____________________________________________________________________________________________________
        // Procesing the orignal Image

        if (fitness_settings.costume_mask == null)                                                                                                // Only calculate the gaussian sobel mask for this stage, if the user is not providing a hand made one
        {
            compute_shaders.gaussian_compute.Dispatch(compute_shaders.gaussian_horizontal_handel,
                                                      original_image.width / 8, original_image.height / 8, 1);

            Graphics.Blit(compute_resources.gaussian_out, compute_resources.original_image_blured);

            compute_shaders.gaussian_compute.Dispatch(compute_shaders.gaussian_vertical_handel,
                                                      original_image.width / 8, original_image.height / 8, 1);
            Graphics.Blit(compute_resources.gaussian_out, compute_resources.original_image_blured);

            compute_shaders.sobel_compute_original.Dispatch(compute_shaders.sobel_handel_original,                                               // Apply sobel effect on the image once. This doesnt need to be on the loop and only happens once per stage
                                                            original_image.width / 32, original_image.height / 32, 1);

            Graphics.Blit(compute_resources.sobel_out, compute_resources.original_image_gradient);                                               // Copying over the results in a new read only texture. Random access resource types are not compatible with samplers. So I have to make this copy
        }
        else
        {
            Graphics.Blit(fitness_settings.costume_mask, compute_resources.original_image_gradient);
        }

        compute_shaders.construct_position_domain_compute.Dispatch(compute_shaders.Debug_Position_Domain_to_Texture_handel,
                                                                   original_image.width / 8, original_image.height / 8, 1);

        Graphics.Blit(compute_resources.debug_texture, evl_man_lazy_ref.current_search_domain_visualisation);

        compute_shaders.construct_position_domain_compute.Dispatch(compute_shaders.Construct_Position_Domain_handel,
                                                                   original_image.width / 8, original_image.height / 8, 1);


        ComputeBuffer.CopyCount(compute_resources.position_domain_buffer, compute_resources.positon_domain_arguments_buffer, 0);

        int[] counter = new int[4];

        compute_resources.positon_domain_arguments_buffer.GetData(counter);

        Debug.Log(string.Format("The number of pixels in the position domains is: {0}", counter[0]));


        // ____________________________________________________________________________________________________
        // CPU Arrays initalization

        // -----------------------
        // Population Pool first gen initialization

        int total_number_of_genes = (int)(evolution_settings.populationPoolNumber * evolution_settings.maximumNumberOfBrushStrokes);

        if (total_number_of_genes % 32 != 0)
        {
            Debug.LogError("Total number of genes is not a multply of 128. Either change the compute dispatches in " +
                           "script, or make sure the Population pool number times the maximum brush strokes number is a multiple of 128");
        }

        if (!is_black_white)
        {
            compute_shaders.compute_selection_functions.Dispatch(                                                                             // Generates the first batch of genes (brush strokes)
                compute_shaders.populate_population, total_number_of_genes / 128, 1, 1);
        }
        else
        {
            compute_shaders.compute_selection_functions.Dispatch(                                                                             // does the same but it only does it in black and white
                compute_shaders.populate_population_BW, total_number_of_genes / 128, 1, 1);
        }


        // ____________________________________________________________________________________________________
        // Command Buffer Generation

        stage_command_buffer = new CommandBuffer
        {
            name = string.Format("Stage_{0}_Command_Buffer", scale_stage_id.ToString()),
        };

        ClearAllRenderTargets(ref stage_command_buffer, true, true, Color.white);

        if (ImageToReproduce.width % 32 != 0 || ImageToReproduce.height % 32 != 0)
        {
            Debug.LogError("image is not multiply of 32. Either change the image dimensions or" +
                           "The threadnumbers set up in the compute shaders!");
        }

        // -----------------------
        // Command Buffer Recording

        uint shader_population_member_begining_index = 0;

        for (int i = 0; i < evolution_settings.populationPoolNumber; i++)
        {
            stage_command_buffer.SetGlobalInt("_memember_begin_stride", (int)shader_population_member_begining_index);                         // This is used in the PopulationShader to sample the currect population member (brush stroke) from the population pool list. It is baisicly population_member_index * genes_number_per_population
            shader_population_member_begining_index += evolution_settings.maximumNumberOfBrushStrokes;                                         // add the genes number (stride) in every loop iteration instead of caculating  population_member_index * genes_number_per_population every time

            // -----------------------
            // Draw Population Pool Member

            ClearAllRenderTargets(ref stage_command_buffer, true, true, Color.white);                                                          // Each stage paints on top of the result of the previus stage, this is where the result of the preveus stage is first blitted on the screen
            stage_command_buffer.DrawProcedural(Matrix4x4.identity, rendering_material, 0,                                                     // Actual drawing happens here. The population is drawn to a render target for further assement down the pipeline
                                                MeshTopology.Triangles, (int)evolution_settings.maximumNumberOfBrushStrokes * 6);

            // -----------------------
            // Copy results for compute

            stage_command_buffer.CopyTexture(compute_resources.active_texture_target, compute_resources.compute_forged_in_render_texture);     // Without copying the rendering results to a new buffer, I was getting weird results after the rendering of the first population member. Seemed like unity unbinds this buffer since it thinks another operation is writing to it and binds a compeletly different buffer as input (auto generated one). The problem is gone if you copy the buffer


            // -----------------------
            //Compute Fitness
            stage_command_buffer.SetGlobalInt("_population_id_handel", i);                                                                     // the id is used in compute to know which of the populatioin members are currently being dealt with.

            // thread groups are made up 32 in 32 threads. The image should be a multiply of 32.
            // so ideally padded to a power of 2. For other image dimensions, the threadnums
            // should be changed in the compute shader for the kernels as well as here the
            // height or width divided by 32. Change it only if you know what you are doing
            stage_command_buffer.DispatchCompute(compute_shaders.compute_fitness_function, compute_shaders.per_pixel_fitness_kernel_handel,    // Compute the fittness of each individual pixel
                                                 ImageToReproduce.width / 32, ImageToReproduce.height / 32, 1);

            // dispatch one compute per row in groups of 64
            stage_command_buffer.DispatchCompute(compute_shaders.compute_fitness_function, compute_shaders.sun_rows_kernel_handel,             // Sum up each row to a single value. So a new array will come out of this which is a single column that has as many member as the height of the image
                                                 ImageToReproduce.height / 32, 1, 1);

            // dispatch a single thread
            stage_command_buffer.DispatchCompute(compute_shaders.compute_fitness_function, compute_shaders.sun_column_kernel_handel,           // sums up all the values of the column which are holding the entire contribution of the row in one float. This float is the sum of the fitness of the entire image
                                                 1, 1, 1);
        }

        // -----------------------
        // Convert Fitness to accumlative weighted probablities

        // Dispatch single thread.
        stage_command_buffer.DispatchCompute(compute_shaders.compute_selection_functions,                                                      // dispatching only a single thread is a waste of a wave front and generally gpu resrources. There  are better reduction algorithmns designed for GPU, have a look at those
                                             compute_shaders.trans_fitness_to_prob_handel, 1, 1, 1);

        // -----------------------
        // Redraw The Fittest of the Population Members
        ClearAllRenderTargets(ref stage_command_buffer, true, true, Color.white);

        stage_command_buffer.DrawProcedural(Matrix4x4.identity, fittest_rendering_material, 0,
                                            MeshTopology.Triangles, (int)evolution_settings.maximumNumberOfBrushStrokes * 6);
        stage_command_buffer.Blit(compute_resources.active_texture_target, BuiltinRenderTextureType.CameraTarget);                             // This is done for the visualisation porpuses, so that you can see the result of the descend real time

        if (evolution_settings.populationPoolNumber % 16 != 0)
        {
            Debug.LogError("The population pool number is set to" + evolution_settings.populationPoolNumber +
                           "Which is not multiple of 32. Either change this number or numThreads in the compute shader!");
        }

        // -----------------------
        // Select parents for each member of the second generation pool based on the fitness of each member

        stage_command_buffer.DispatchCompute(compute_shaders.compute_selection_functions,
                                             compute_shaders.parent_selection_handel, (int)evolution_settings.populationPoolNumber / 16, 1, 1);

        if (total_number_of_genes % 128 != 0)
        {
            Debug.LogError(string.Format("Total number of genes in the population pool is: {0}, which is not a factor of 128. " +
                                         "Either change this number to a factor of 128 or change the numThreads in the compute shader"));
        }

        // -----------------------
        // Create each member of the second generation by combining the genes of the parents

        stage_command_buffer.DispatchCompute(compute_shaders.compute_selection_functions,
                                             compute_shaders.cross_over_handel, total_number_of_genes / 128, 1, 1);

        if (!is_black_white)
        {
            stage_command_buffer.DispatchCompute(compute_shaders.compute_selection_functions,                                                  // This copies the cross overed genes from second gen to the main buffer for rendering in next frame and also mutates some of them
                                                 compute_shaders.mutation_and_copy_handel, total_number_of_genes / 128, 1, 1);
        }
        else
        {
            stage_command_buffer.DispatchCompute(compute_shaders.compute_selection_functions,                                                  // This copies the cross overed genes from second gen to the main buffer for rendering in next frame and also mutates some of them. However mutation is in black and white
                                                 compute_shaders.mutation_and_copy_BW_handel, total_number_of_genes / 128, 1, 1);
        }

        Camera.main.AddCommandBuffer(CameraEvent.AfterEverything, stage_command_buffer);                                                       // Evolution Master has already checked that the Camera main is valid. Dispatch the Command buffer after everything
    }