首页 > 其他分享 >Stanford CS149 -- Assignment 3: A Simple CUDA Renderer

Stanford CS149 -- Assignment 3: A Simple CUDA Renderer

时间:2024-10-14 21:26:01浏览次数:1  
标签:__ CS149 blockDim -- Assignment float cuConstRendererParams uint imageWidth

作业描述及代码参见:CS149-asst3

实验环境:WSL2;GeForce MX350;Cuda 12.6

第一部分:CUDA 热身练习 1:SAXPY

实验结果:

SAXPY

相比基于 CPU 的实现,性能明显下降。这是由于 SAXPY 属于 I/O 密集型任务,计算量较小,主要的时间耗费在数据的转移。

第二部分:CUDA 热身练习 2:并行前缀和

scan

find_repeats

第三部分:简单的圆形渲染器

渲染时,圆渲染的先后关系会影响正确性。初始错误实现中,CudaRenderer::render() 中的 kernal launch 是为每个圆分配一个线程并行渲染,渲染的先后关系及原子性要求均无法满足。

渲染器两个潜在的并行性轴:像素的并行性和圆的并行性。为每个圆分配一个线程并行渲染无法满足要求,那就改为为每个像素分配一个线程。每个线程内按顺序渲染圆。由此得到以下正确实现:

__global__ void kernelRenderPixels() {
    int pixelX = blockDim.x * blockIdx.x + threadIdx.x;
    int pixelY = blockDim.y * blockIdx.y + threadIdx.y;
    short imageWidth = cuConstRendererParams.imageWidth;
    short imageHeight = cuConstRendererParams.imageHeight;
    if (pixelX >= imageWidth || pixelY >= imageHeight)
        return;

    float invWidth = 1.f / imageWidth;
    float invHeight = 1.f / imageHeight;
    float2 pixelCenterNorm = make_float2(invWidth * (static_cast<float>(pixelX) + 0.5f),
                                         invHeight * (static_cast<float>(pixelY) + 0.5f));
    float4* imgPtr = (float4*)(&cuConstRendererParams.imageData[4 * (pixelY * imageWidth + pixelX)]);

    int numCircles = cuConstRendererParams.numCircles;
    for (int i = 0; i < numCircles; ++i) {
        float3 p = *(float3*)(&cuConstRendererParams.position[3 * i]);
        shadePixel(i, pixelCenterNorm, p, imgPtr);
    }
}

void CudaRenderer::render() {
    dim3 blockDim(16, 16);
    dim3 gridDim(
        (image->width + blockDim.x - 1) / blockDim.x,
        (image->height + blockDim.y - 1) / blockDim.y);
    kernelRenderPixels<<<gridDim, blockDim>>>();
    cudaDeviceSynchronize();
}

以上实现正确,但性能较差,原因在于每个线程内都顺序计算每个圆对单个像素颜色的贡献,没有充分利用圆的并行性。

考虑实际情况,对于一个圆来说,图片中的大部分像素可能都不需要渲染。优化方法是每个 block 内线程使用共享内存共同配合完成圆圈的初步筛选。由于每个线程负责一个像素,一个 block 内的所有线程负责的则是图片的一个矩形区域,如果一个圆和这个矩形区域不相交,则说明该区域内的像素均不需要渲染,因此,在渲染每个像素时,只需遍历与该矩形区域相交的圆即可。

计算与子区域有相交的圆,可以采用和第二部分类似的算法,使用并行前缀和实现。

__global__ void kernelRenderPixels() {
    uint idx = blockDim.x * threadIdx.y + threadIdx.x;
    uint pixelX = blockDim.x * blockIdx.x + threadIdx.x;
    uint pixelY = blockDim.y * blockIdx.y + threadIdx.y;
    short imageWidth = (short)cuConstRendererParams.imageWidth;
    short imageHeight = (short)cuConstRendererParams.imageHeight;

    float invWidth = 1.f / (float)imageWidth;
    float invHeight = 1.f / (float)imageHeight;
	
    // 线程块负责的矩形区域
    uint boxL = blockDim.x * blockIdx.x;
    uint boxR = boxL + blockDim.x < imageWidth ? boxL + blockDim.x : imageWidth;
    uint boxB = blockDim.y * blockIdx.y;
    uint boxT = boxB + blockDim.y < imageHeight ? boxB + blockDim.y : imageHeight;

    float boxLNorm = (float)boxL * invWidth;
    float boxRNorm = (float)boxR * invWidth;
    float boxBNorm = (float)boxB * invHeight;
    float boxTNorm = (float)boxT * invHeight;

    __shared__ uint flag[BLOCKSIZE];
    __shared__ uint presum[BLOCKSIZE];
    __shared__ uint scratch[BLOCKSIZE * 2];
    __shared__ uint circles[BLOCKSIZE];

    float2 pixelCenterNorm = make_float2(invWidth * (static_cast<float>(pixelX) + 0.5f),
                                         invHeight * (static_cast<float>(pixelY) + 0.5f));
    float4* imgPtr = (float4*)(&cuConstRendererParams.imageData[4 * (pixelY * imageWidth + pixelX)]);

    int numCircles = cuConstRendererParams.numCircles;
    for (int i = 0; i < numCircles; i += BLOCKSIZE) {
        uint circleIdx = i + idx;
        // 本线程负责检查的圆的序号
        // 检查圆是否与该区域有交叉
        if (circleIdx < numCircles) {
            float3 p = *(float3*)(&cuConstRendererParams.position[3 * circleIdx]);
            flag[idx] = circleInBoxConservative(p.x, p.y,
                cuConstRendererParams.radius[circleIdx],
                boxLNorm, boxRNorm, boxTNorm, boxBNorm);
        } else {
            flag[idx] = 0;
        }
        __syncthreads();

        // 计算flag的前缀和
        sharedMemExclusiveScan((int)idx, flag, presum, scratch, BLOCKSIZE);
        __syncthreads();

        // 获取所有可能和区域相交的圆
        if (flag[idx])
            circles[presum[idx]] = circleIdx;
        __syncthreads();

        // 进行渲染
        if (pixelX < imageWidth && pixelY < imageHeight) {
            uint num = presum[BLOCKSIZE - 1] + flag[BLOCKSIZE - 1]; // 可能和区域相交的圆的数量
            for (int j = 0; j < num; ++j) {
                float3 p = *(float3*)(&cuConstRendererParams.position[3 * circles[j]]);
                shadePixel((int)circles[j], pixelCenterNorm, p, imgPtr);
            }
        }
    }
}

render

标签:__,CS149,blockDim,--,Assignment,float,cuConstRendererParams,uint,imageWidth
From: https://www.cnblogs.com/bienboy/p/18466158

相关文章

  • Mybatis的Mapper映射文件中常用标签
    "mapper":是整个映射文件的根元素,包含了所有的其他标签,有一个重要的属性:namespace,用来指定映射文件对应的接口的全限定名,保证多个映射文件中使用相同的ID不会产生冲突,因为每个ID都是基于其命名空间唯一的点击查看代码<mappernamespace="com.example.mapper.Use......
  • 可视化流程图的UI设计展示参考
              ......
  • 【qt】一个动画实现
    基于https://www.bilibili.com/video/BV1Li421Y7EH/?spm_id_from=333.999.top_right_bar_window_history.content.click原理的一个qt实现#pragmaonce#include<QWidget>#include"ui_Worm.h"#include<QPointF>#include<QList>#include<QPai......
  • 解决 Maven 插件报错:The plugin org.codehaus.mojo:flatten-maven-plugin:1.5.0 requi
    检查Maven版本:首先,确认当前使用的Maven版本是否与插件要求的版本一致。可以通过在命令行中输入 mvn-v 来查看当前Maven的版本信息。升级或降级Maven版本:如果当前Maven版本过低,需要升级到插件要求的版本;如果过高,可能需要降级。升级或降级Maven可以参考Maven的官......
  • 每日总结001
    一.今天中午看了一本小说,看得着迷,然后很急切的想知道后续剧情,如果之前遇到这种情况,我会毫不犹豫的继续追,但是今天我做出了改变,我努力说服自己沉迷于小说会浪费很多宝贵的时间,我应该适可而止,并且下午还有课,最终我克服了自己的欲望,选择了休息。二.下午两点上课,中午1.15的闹钟响了......
  • Stanford CS149 -- Assignment 4: NanoGPT149
    作业描述及代码参见:cs149gptWarm-Up:访问张量张量/数组都是按行存储的,四维数组可以看作元素为三维数组的数组,元素大小即为三维数组内元素总数,以此类推。第1部分:简单(但不太高效)的注意力机制实现主要实现两个矩阵乘法和一个softmax运算。第2部分:块矩阵乘法和UnfusedSof......
  • Mybatis的Mapper映射文件中常用标签及作用
    MyBatis的Mapper映射文件是一种XML格式的配置文件,它用于定义SQL语句和Java对象之间的映射关系。以下是一些常用的标签及其作用。!DOCTYPEmapperPUBLIC#定义文档类型和公共标识符,用于XML文档的开头。<mapper>#根标签,定义一个映射文件。<namespace>#定义映射文件的命......
  • Mybatis的Mapper映射文件中常用标签及作用
    1、<mapper>:根元素,表示一个Mapper接口的配置。2、<select>:用于编写sql查询语句。3、<insert>:用于编写sql插入数据的<details>4、<update>:用于编写sql更新数据的语句。5、<delete>:用于编写sql删除数据的语句。6、<resultMap>:定义了如何将数据库中的列与Java对象的属......
  • Mybatis的Mapper映射文件中常用标签
    select点击查看代码<selectid="selectPerson"parameterType="int"parameterMap="deprecated"resultType="hashmap"resultMap="personResultMap"flushCache="false"useCache="true"time......
  • 实验二
    任务一:源代码:`#include<stdio.h>include<stdlib.h>include<time.h>defineN5defineN1397defineN2476defineN321intmain(){intcnt;intrandom_major,random_no;srand(time(NULL));cnt=0;while(cnt<N){random_major=rand(......