使用 JOCL/OpenCL 将图像复制到另一个图像上

如何解决使用 JOCL/OpenCL 将图像复制到另一个图像上

所以我的目标是将 GPU 用于我全新的 Java 项目,该项目将创建游戏和游戏引擎本身(我认为这是深入了解其工作原理的一种很好的方式)。

我在带有 java.awt.Graphics2D 的 CPU 上使用多线程来显示我的游戏,但我在其他 PC 上观察到游戏运行速度低于 40FPS,因此我决定学习如何使用 GPU (我仍然会在 for 循环中渲染所有对象,然后在屏幕上绘制图像)。

出于这个原因,我开始按照 OpenCL 文档编写代码,JOCL 示例了一个简单的小测试,即将纹理绘制到背景图像上(假设每个实体都有一个纹理) .

这个方法在每次渲染调用中被调用,它被赋予背景、纹理和这个实体的位置作为参数。

以下两个代码都已更新以符合@ProjectPhysX 的建议。

public static void XXX(final BufferedImage output_image,final BufferedImage input_image,float x,float y) {
        cl_image_format format = new cl_image_format();
        format.image_channel_order = CL_RGBA;
        format.image_channel_data_type = CL_UNSIGNED_INT8;

        //allocate ouput pointer
        cl_image_desc output_description = new cl_image_desc();
        output_description.buffer = null; //must be null for 2D image
        output_description.image_depth = 0; //is only used if the image is a 3D image
        output_description.image_row_pitch = 0; //must be 0 if host_ptr is null
        output_description.image_slice_pitch = 0; //must be 0 if host_ptr is null
        output_description.num_mip_levels = 0; //must be 0
        output_description.num_samples = 0; //must be 0
        output_description.image_type = CL_MEM_OBJECT_IMAGE2D;
        output_description.image_width = output_image.getWidth();
        output_description.image_height = output_image.getHeight();
        output_description.image_array_size = output_description.image_width * output_description.image_height;

        cl_mem output_memory = clCreateImage(context,CL_MEM_WRITE_ONLY,format,output_description,null,null);
        
        //set up first kernel arg
        clSetKernelArg(kernel,Sizeof.cl_mem,Pointer.to(output_memory));
        
        //allocates input pointer
        cl_image_desc input_description = new cl_image_desc();
        input_description.buffer = null; //must be null for 2D image
        input_description.image_depth = 0; //is only used if the image is a 3D image
        input_description.image_row_pitch = 0; //must be 0 if host_ptr is null
        input_description.image_slice_pitch = 0; //must be 0 if host_ptr is null
        input_description.num_mip_levels = 0; //must be 0
        input_description.num_samples = 0; //must be 0
        input_description.image_type = CL_MEM_OBJECT_IMAGE2D;
        input_description.image_width = input_image.getWidth();
        input_description.image_height = input_image.getHeight();
        input_description.image_array_size = input_description.image_width * input_description.image_height;

        DataBufferInt input_buffer = (DataBufferInt) input_image.getRaster().getDataBuffer();
        int input_data[] = input_buffer.getData();

        cl_mem input_memory = clCreateImage(context,CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,input_description,Pointer.to(input_data),null);

        //loads the input buffer to the gpu memory
        long[] input_origin = new long[] { 0,0 };
        long[] input_region = new long[] { input_image.getWidth(),input_image.getHeight(),1 };
        int input_row_pitch = input_image.getWidth() * Sizeof.cl_uint; //the length of each row in bytes
        clEnqueueWriteImage(commandQueue,input_memory,CL_TRUE,input_origin,input_region,input_row_pitch,null);
        
        //set up second kernel arg
        clSetKernelArg(kernel,1,Pointer.to(input_memory));

        //set up third and fourth kernel args
        clSetKernelArg(kernel,2,Sizeof.cl_float,Pointer.to(new float[] { x }));
        clSetKernelArg(kernel,3,Pointer.to(new float[] { y }));
        
        //blocks until all previously queued commands are issued
        clFinish(commandQueue);

        //enqueue the program execution
        long[] globalWorkSize = new long[] { input_description.image_width,input_description.image_height };
        clEnqueueNDRangeKernel(commandQueue,kernel,globalWorkSize,null);

        //transfer the output result back to host
        DataBufferInt output_buffer = (DataBufferInt) output_image.getRaster().getDataBuffer();
        int output_data[] = output_buffer.getData();
        long[] output_origin = new long[] { 0,0 };
        long[] output_region = new long[] { output_description.image_width,output_description.image_height,1 };
        int output_row_pitch = output_image.getWidth() * Sizeof.cl_uint;
        clEnqueueReadImage(commandQueue,output_memory,output_origin,output_region,output_row_pitch,Pointer.to(output_data),null);

        //free pointers
        clReleaseMemObject(input_memory);
        clReleaseMemObject(output_memory);
    }

这是在内核上运行的程序源代码。

const sampler_t 采样器 = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;

__kernel void drawImage(__write_only image2d_t dst_image,__read_only image2d_t src_image,float xoff,float yoff)
{
    const int x = get_global_id(0);
    const int y = get_global_id(1);

    int2 in_coords = (int2) { x,y };

    uint4 pixel = read_imageui(src_image,sampler,in_coords);
    pixel = -16184301;
    printf("%d,%d,%u\n",x,y,pixel);

    const int sx = get_global_size(0);
    const int sy = get_global_size(1);

    int2 out_coords = (int2) { ((int) xoff + x) % sx,((int) yoff + y) % sy};
    
    write_imageui(dst_image,out_coords,pixel);
}

没有调用write_imageui,背景被涂成黑色,否则是白色。 目前,我有点难以理解为什么在 C 函数中像素 = 0,但我认为熟悉 JOCL 的人会很快发现我在这段代码中的错误。今天,也许明天,我对这段代码感到非常困惑,但我觉得我永远不会发现自己的错误。出于这个原因,我请求您帮助审查我的代码。我觉得自己像个白痴,当时我想不通。

解决方法

试试

    const int sx = get_global_size(0);
    const int sy = get_global_size(1);
    int2 out_coords = (int2) { (xoff + x)%sx,(yoff + y)%sy};

避免错误或未定义的行为。如果坐标+偏移量放在图像区域旁边,现在您正在写入 Nirwana。此外,在调用内核之前没有 clEnqueueWriteImage,因此 GPU 上的 src_image 未定义并且可能包含随机值。

OpenCL 要求在 global 内存空间中声明内核参数:

__kernel void drawImage(global image2d_t dst_image,global image2d_t src_image,global float xoff,global float yoff)

另外,作为用 Java、C++ 和 GPU 并行化 OpenCL 编写图形引擎的人,让我给您一些指导:在 Java 代码中,您可能使用画家算法:列出所有绘制的对象及其对象近似 z 坐标,按 z 坐标对对象进行排序,并在单个 for 循环中从后到前绘制它们。在 GPU 上,画家的算法将不起作用,因为您无法对其进行并行化。取而代之的是,您在 3D 空间中有一个对象(线/三角形)列表,并在此列表上并行化:每个 GPU 线程同时光栅化一个三角形,所有线程,并同时在帧上绘制像素。要解决排水顺序问题,您可以使用 z 缓冲区:由每个像素的 z 坐标组成的图像。在线条/三角形光栅化过程中,您计算​​每个像素的 z 坐标,并且只有当它大于之前在 z 缓冲区中该像素处的坐标时,才绘制新颜色。

关于性能:java.awt.Graphics2D 在 CPU 使用率方面非常高效,您可以以 60fps 的速度每帧处理约 40k 个三角形。使用 OpenCL,预计每帧约 30M 个三角形,60fps。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res